| /* |
| * Copyright 2011 Christoph Bumiller |
| * |
| * 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 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 __NV50_IR_UTIL_H__ |
| #define __NV50_IR_UTIL_H__ |
| |
| #include <new> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <memory> |
| #include <map> |
| |
| #ifndef NDEBUG |
| # include <typeinfo> |
| #endif |
| |
| #include "util/u_inlines.h" |
| #include "util/u_memory.h" |
| |
| #define ERROR(args...) debug_printf("ERROR: " args) |
| #define WARN(args...) debug_printf("WARNING: " args) |
| #define INFO(args...) debug_printf(args) |
| |
| #define INFO_DBG(m, f, args...) \ |
| do { \ |
| if (m & NV50_IR_DEBUG_##f) \ |
| debug_printf(args); \ |
| } while(0) |
| |
| #define FATAL(args...) \ |
| do { \ |
| fprintf(stderr, args); \ |
| abort(); \ |
| } while(0) |
| |
| |
| #define NV50_IR_FUNC_ALLOC_OBJ_DEF(obj, f, args...) \ |
| new ((f)->getProgram()->mem_##obj.allocate()) obj(f, args) |
| |
| #define new_Instruction(f, args...) \ |
| NV50_IR_FUNC_ALLOC_OBJ_DEF(Instruction, f, args) |
| #define new_CmpInstruction(f, args...) \ |
| NV50_IR_FUNC_ALLOC_OBJ_DEF(CmpInstruction, f, args) |
| #define new_TexInstruction(f, args...) \ |
| NV50_IR_FUNC_ALLOC_OBJ_DEF(TexInstruction, f, args) |
| #define new_FlowInstruction(f, args...) \ |
| NV50_IR_FUNC_ALLOC_OBJ_DEF(FlowInstruction, f, args) |
| |
| #define new_LValue(f, args...) \ |
| NV50_IR_FUNC_ALLOC_OBJ_DEF(LValue, f, args) |
| |
| |
| #define NV50_IR_PROG_ALLOC_OBJ_DEF(obj, p, args...) \ |
| new ((p)->mem_##obj.allocate()) obj(p, args) |
| |
| #define new_Symbol(p, args...) \ |
| NV50_IR_PROG_ALLOC_OBJ_DEF(Symbol, p, args) |
| #define new_ImmediateValue(p, args...) \ |
| NV50_IR_PROG_ALLOC_OBJ_DEF(ImmediateValue, p, args) |
| |
| |
| #define delete_Instruction(p, insn) (p)->releaseInstruction(insn) |
| #define delete_Value(p, val) (p)->releaseValue(val) |
| |
| |
| namespace nv50_ir { |
| |
| class Iterator |
| { |
| public: |
| virtual ~Iterator() { }; |
| virtual void next() = 0; |
| virtual void *get() const = 0; |
| virtual bool end() const = 0; // if true, get will return 0 |
| virtual void reset() { assert(0); } // only for graph iterators |
| }; |
| |
| typedef std::auto_ptr<Iterator> IteratorRef; |
| |
| class ManipIterator : public Iterator |
| { |
| public: |
| virtual bool insert(void *) = 0; // insert after current position |
| virtual void erase() = 0; |
| }; |
| |
| // WARNING: do not use a->prev/next for __item or __list |
| |
| #define DLLIST_DEL(__item) \ |
| do { \ |
| (__item)->prev->next = (__item)->next; \ |
| (__item)->next->prev = (__item)->prev; \ |
| (__item)->next = (__item); \ |
| (__item)->prev = (__item); \ |
| } while(0) |
| |
| #define DLLIST_ADDTAIL(__list, __item) \ |
| do { \ |
| (__item)->next = (__list); \ |
| (__item)->prev = (__list)->prev; \ |
| (__list)->prev->next = (__item); \ |
| (__list)->prev = (__item); \ |
| } while(0) |
| |
| #define DLLIST_ADDHEAD(__list, __item) \ |
| do { \ |
| (__item)->prev = (__list); \ |
| (__item)->next = (__list)->next; \ |
| (__list)->next->prev = (__item); \ |
| (__list)->next = (__item); \ |
| } while(0) |
| |
| #define DLLIST_MERGE(__listA, __listB, ty) \ |
| do { \ |
| ty prevB = (__listB)->prev; \ |
| (__listA)->prev->next = (__listB); \ |
| (__listB)->prev->next = (__listA); \ |
| (__listB)->prev = (__listA)->prev; \ |
| (__listA)->prev = prevB; \ |
| } while(0) |
| |
| #define DLLIST_EMPTY(__list) ((__list)->next == (__list)) |
| |
| #define DLLIST_FOR_EACH(list, it) \ |
| for (DLList::Iterator (it) = (list)->iterator(); !(it).end(); (it).next()) |
| |
| class DLList |
| { |
| public: |
| class Item |
| { |
| public: |
| Item(void *priv) : next(this), prev(this), data(priv) { } |
| |
| public: |
| Item *next; |
| Item *prev; |
| void *data; |
| }; |
| |
| DLList() : head(0) { } |
| ~DLList() { clear(); } |
| |
| inline void insertHead(void *data) |
| { |
| Item *item = new Item(data); |
| |
| assert(data); |
| |
| item->prev = &head; |
| item->next = head.next; |
| head.next->prev = item; |
| head.next = item; |
| } |
| |
| inline void insertTail(void *data) |
| { |
| Item *item = new Item(data); |
| |
| assert(data); |
| |
| DLLIST_ADDTAIL(&head, item); |
| } |
| |
| inline void insert(void *data) { insertTail(data); } |
| |
| void clear(); |
| |
| class Iterator : public ManipIterator |
| { |
| public: |
| Iterator(Item *head, bool r) : rev(r), pos(r ? head->prev : head->next), |
| term(head) { } |
| |
| virtual void next() { if (!end()) pos = rev ? pos->prev : pos->next; } |
| virtual void *get() const { return pos->data; } |
| virtual bool end() const { return pos == term; } |
| |
| // caution: if you're at end-2 and erase it, then do next, you're at end |
| virtual void erase(); |
| virtual bool insert(void *data); |
| |
| // move item to a another list, no consistency with its iterators though |
| void moveToList(DLList&); |
| |
| private: |
| const bool rev; |
| Item *pos; |
| Item *term; |
| |
| friend class DLList; |
| }; |
| |
| inline void erase(Iterator& pos) |
| { |
| pos.erase(); |
| } |
| |
| Iterator iterator() |
| { |
| return Iterator(&head, false); |
| } |
| |
| Iterator revIterator() |
| { |
| return Iterator(&head, true); |
| } |
| |
| private: |
| Item head; |
| }; |
| |
| class Stack |
| { |
| public: |
| class Item { |
| public: |
| union { |
| void *p; |
| int i; |
| unsigned int u; |
| float f; |
| double d; |
| } u; |
| |
| Item() { memset(&u, 0, sizeof(u)); } |
| }; |
| |
| Stack() : size(0), limit(0), array(0) { } |
| ~Stack() { if (array) FREE(array); } |
| |
| inline void push(int i) { Item data; data.u.i = i; push(data); } |
| inline void push(unsigned int u) { Item data; data.u.u = u; push(data); } |
| inline void push(void *p) { Item data; data.u.p = p; push(data); } |
| inline void push(float f) { Item data; data.u.f = f; push(data); } |
| |
| inline void push(Item data) |
| { |
| if (size == limit) |
| resize(); |
| array[size++] = data; |
| } |
| |
| inline Item pop() |
| { |
| if (!size) { |
| Item data; |
| assert(0); |
| return data; |
| } |
| return array[--size]; |
| } |
| |
| inline unsigned int getSize() { return size; } |
| |
| inline Item& peek() { assert(size); return array[size - 1]; } |
| |
| void clear(bool releaseStorage = false) |
| { |
| if (releaseStorage && array) |
| FREE(array); |
| size = limit = 0; |
| } |
| |
| void moveTo(Stack&); // move all items to target (not like push(pop())) |
| |
| private: |
| void resize() |
| { |
| unsigned int sizeOld, sizeNew; |
| |
| sizeOld = limit * sizeof(Item); |
| limit = MAX2(4, limit + limit); |
| sizeNew = limit * sizeof(Item); |
| |
| array = (Item *)REALLOC(array, sizeOld, sizeNew); |
| } |
| |
| unsigned int size; |
| unsigned int limit; |
| Item *array; |
| }; |
| |
| class DynArray |
| { |
| public: |
| class Item |
| { |
| public: |
| union { |
| uint32_t u32; |
| void *p; |
| }; |
| }; |
| |
| DynArray() : data(NULL), size(0) { } |
| |
| ~DynArray() { if (data) FREE(data); } |
| |
| inline Item& operator[](unsigned int i) |
| { |
| if (i >= size) |
| resize(i); |
| return data[i]; |
| } |
| |
| inline const Item operator[](unsigned int i) const |
| { |
| return data[i]; |
| } |
| |
| void resize(unsigned int index) |
| { |
| const unsigned int oldSize = size * sizeof(Item); |
| |
| if (!size) |
| size = 8; |
| while (size <= index) |
| size <<= 1; |
| |
| data = (Item *)REALLOC(data, oldSize, size * sizeof(Item)); |
| } |
| |
| void clear() |
| { |
| FREE(data); |
| data = NULL; |
| size = 0; |
| } |
| |
| private: |
| Item *data; |
| unsigned int size; |
| }; |
| |
| class ArrayList |
| { |
| public: |
| ArrayList() : size(0) { } |
| |
| void insert(void *item, int& id) |
| { |
| id = ids.getSize() ? ids.pop().u.i : size++; |
| data[id].p = item; |
| } |
| |
| void remove(int& id) |
| { |
| const unsigned int uid = id; |
| assert(uid < size && data[id].p); |
| ids.push(uid); |
| data[uid].p = NULL; |
| id = -1; |
| } |
| |
| inline int getSize() const { return size; } |
| |
| inline void *get(unsigned int id) { assert(id < size); return data[id].p; } |
| |
| class Iterator : public nv50_ir::Iterator |
| { |
| public: |
| Iterator(const ArrayList *array) : pos(0), data(array->data) |
| { |
| size = array->getSize(); |
| if (size) |
| nextValid(); |
| } |
| |
| void nextValid() { while ((pos < size) && !data[pos].p) ++pos; } |
| |
| void next() { if (pos < size) { ++pos; nextValid(); } } |
| void *get() const { assert(pos < size); return data[pos].p; } |
| bool end() const { return pos >= size; } |
| |
| private: |
| unsigned int pos; |
| unsigned int size; |
| const DynArray& data; |
| |
| friend class ArrayList; |
| }; |
| |
| Iterator iterator() const { return Iterator(this); } |
| |
| void clear() |
| { |
| data.clear(); |
| ids.clear(true); |
| size = 0; |
| } |
| |
| private: |
| DynArray data; |
| Stack ids; |
| unsigned int size; |
| }; |
| |
| class Interval |
| { |
| public: |
| Interval() : head(0), tail(0) { } |
| Interval(const Interval&); |
| ~Interval(); |
| |
| bool extend(int, int); |
| void insert(const Interval&); |
| void unify(Interval&); // clears source interval |
| void clear(); |
| |
| inline int begin() const { return head ? head->bgn : -1; } |
| inline int end() const { checkTail(); return tail ? tail->end : -1; } |
| inline bool isEmpty() const { return !head; } |
| bool overlaps(const Interval&) const; |
| bool contains(int pos) const; |
| |
| inline int extent() const { return end() - begin(); } |
| int length() const; |
| |
| void print() const; |
| |
| inline void checkTail() const; |
| |
| private: |
| class Range |
| { |
| public: |
| Range(int a, int b) : next(0), bgn(a), end(b) { } |
| |
| Range *next; |
| int bgn; |
| int end; |
| |
| void coalesce(Range **ptail) |
| { |
| Range *rnn; |
| |
| while (next && end >= next->bgn) { |
| assert(bgn <= next->bgn); |
| rnn = next->next; |
| end = MAX2(end, next->end); |
| delete next; |
| next = rnn; |
| } |
| if (!next) |
| *ptail = this; |
| } |
| }; |
| |
| Range *head; |
| Range *tail; |
| }; |
| |
| class BitSet |
| { |
| public: |
| BitSet() : marker(false), data(0), size(0) { } |
| BitSet(unsigned int nBits, bool zero) : marker(false), data(0), size(0) |
| { |
| allocate(nBits, zero); |
| } |
| ~BitSet() |
| { |
| if (data) |
| FREE(data); |
| } |
| |
| bool allocate(unsigned int nBits, bool zero); |
| bool resize(unsigned int nBits); // keep old data, zero additional bits |
| |
| inline unsigned int getSize() const { return size; } |
| |
| void fill(uint32_t val); |
| |
| void setOr(BitSet *, BitSet *); // second BitSet may be NULL |
| |
| inline void set(unsigned int i) |
| { |
| assert(i < size); |
| data[i / 32] |= 1 << (i % 32); |
| } |
| // NOTE: range may not cross 32 bit boundary (implies n <= 32) |
| inline void setRange(unsigned int i, unsigned int n) |
| { |
| assert((i + n) <= size && (((i % 32) + n) <= 32)); |
| data[i / 32] |= ((1 << n) - 1) << (i % 32); |
| } |
| inline void setMask(unsigned int i, uint32_t m) |
| { |
| assert(i < size); |
| data[i / 32] |= m; |
| } |
| |
| inline void clr(unsigned int i) |
| { |
| assert(i < size); |
| data[i / 32] &= ~(1 << (i % 32)); |
| } |
| // NOTE: range may not cross 32 bit boundary (implies n <= 32) |
| inline void clrRange(unsigned int i, unsigned int n) |
| { |
| assert((i + n) <= size && (((i % 32) + n) <= 32)); |
| data[i / 32] &= ~(((1 << n) - 1) << (i % 32)); |
| } |
| |
| inline bool test(unsigned int i) const |
| { |
| assert(i < size); |
| return data[i / 32] & (1 << (i % 32)); |
| } |
| // NOTE: range may not cross 32 bit boundary (implies n <= 32) |
| inline bool testRange(unsigned int i, unsigned int n) |
| { |
| assert((i + n) <= size && (((i % 32) + n) <= 32)); |
| return data[i / 32] & (((1 << n) - 1) << (i % 32)); |
| } |
| |
| // Find a range of size (<= 32) clear bits aligned to roundup_pow2(size). |
| int findFreeRange(unsigned int size) const; |
| |
| BitSet& operator|=(const BitSet&); |
| |
| BitSet& operator=(const BitSet& set) |
| { |
| assert(data && set.data); |
| assert(size == set.size); |
| memcpy(data, set.data, (set.size + 7) / 8); |
| return *this; |
| } |
| |
| void andNot(const BitSet&); |
| |
| // bits = (bits | setMask) & ~clrMask |
| inline void periodicMask32(uint32_t setMask, uint32_t clrMask) |
| { |
| for (unsigned int i = 0; i < (size + 31) / 32; ++i) |
| data[i] = (data[i] | setMask) & ~clrMask; |
| } |
| |
| unsigned int popCount() const; |
| |
| void print() const; |
| |
| public: |
| bool marker; // for user |
| |
| private: |
| uint32_t *data; |
| unsigned int size; |
| }; |
| |
| void Interval::checkTail() const |
| { |
| #if NV50_DEBUG & NV50_DEBUG_PROG_RA |
| Range *r = head; |
| while (r->next) |
| r = r->next; |
| assert(tail == r); |
| #endif |
| } |
| |
| class MemoryPool |
| { |
| private: |
| inline bool enlargeAllocationsArray(const unsigned int id, unsigned int nr) |
| { |
| const unsigned int size = sizeof(uint8_t *) * id; |
| const unsigned int incr = sizeof(uint8_t *) * nr; |
| |
| uint8_t **alloc = (uint8_t **)REALLOC(allocArray, size, size + incr); |
| if (!alloc) |
| return false; |
| allocArray = alloc; |
| return true; |
| } |
| |
| inline bool enlargeCapacity() |
| { |
| const unsigned int id = count >> objStepLog2; |
| |
| uint8_t *const mem = (uint8_t *)MALLOC(objSize << objStepLog2); |
| if (!mem) |
| return false; |
| |
| if (!(id % 32)) { |
| if (!enlargeAllocationsArray(id, 32)) { |
| FREE(mem); |
| return false; |
| } |
| } |
| allocArray[id] = mem; |
| return true; |
| } |
| |
| public: |
| MemoryPool(unsigned int size, unsigned int incr) : objSize(size), |
| objStepLog2(incr) |
| { |
| allocArray = NULL; |
| released = NULL; |
| count = 0; |
| } |
| |
| ~MemoryPool() |
| { |
| unsigned int allocCount = (count + (1 << objStepLog2) - 1) >> objStepLog2; |
| for (unsigned int i = 0; i < allocCount && allocArray[i]; ++i) |
| FREE(allocArray[i]); |
| if (allocArray) |
| FREE(allocArray); |
| } |
| |
| void *allocate() |
| { |
| void *ret; |
| const unsigned int mask = (1 << objStepLog2) - 1; |
| |
| if (released) { |
| ret = released; |
| released = *(void **)released; |
| return ret; |
| } |
| |
| if (!(count & mask)) |
| if (!enlargeCapacity()) |
| return NULL; |
| |
| ret = allocArray[count >> objStepLog2] + (count & mask) * objSize; |
| ++count; |
| return ret; |
| } |
| |
| void release(void *ptr) |
| { |
| *(void **)ptr = released; |
| released = ptr; |
| } |
| |
| private: |
| uint8_t **allocArray; // array (list) of MALLOC allocations |
| |
| void *released; // list of released objects |
| |
| unsigned int count; // highest allocated object |
| |
| const unsigned int objSize; |
| const unsigned int objStepLog2; |
| }; |
| |
| /** |
| * Composite object cloning policy. |
| * |
| * Encapsulates how sub-objects are to be handled (if at all) when a |
| * composite object is being cloned. |
| */ |
| template<typename C> |
| class ClonePolicy |
| { |
| protected: |
| C *c; |
| |
| public: |
| ClonePolicy(C *c) : c(c) {} |
| |
| C *context() { return c; } |
| |
| template<typename T> T *get(T *obj) |
| { |
| void *clone = lookup(obj); |
| if (!clone) |
| clone = obj->clone(*this); |
| return reinterpret_cast<T *>(clone); |
| } |
| |
| template<typename T> void set(const T *obj, T *clone) |
| { |
| insert(obj, clone); |
| } |
| |
| protected: |
| virtual void *lookup(void *obj) = 0; |
| virtual void insert(const void *obj, void *clone) = 0; |
| }; |
| |
| /** |
| * Shallow non-recursive cloning policy. |
| * |
| * Objects cloned with the "shallow" policy don't clone their |
| * children recursively, instead, the new copy shares its children |
| * with the original object. |
| */ |
| template<typename C> |
| class ShallowClonePolicy : public ClonePolicy<C> |
| { |
| public: |
| ShallowClonePolicy(C *c) : ClonePolicy<C>(c) {} |
| |
| protected: |
| virtual void *lookup(void *obj) |
| { |
| return obj; |
| } |
| |
| virtual void insert(const void *obj, void *clone) |
| { |
| } |
| }; |
| |
| template<typename C, typename T> |
| inline T *cloneShallow(C *c, T *obj) |
| { |
| ShallowClonePolicy<C> pol(c); |
| return obj->clone(pol); |
| } |
| |
| /** |
| * Recursive cloning policy. |
| * |
| * Objects cloned with the "deep" policy clone their children |
| * recursively, keeping track of what has already been cloned to |
| * avoid making several new copies of the same object. |
| */ |
| template<typename C> |
| class DeepClonePolicy : public ClonePolicy<C> |
| { |
| public: |
| DeepClonePolicy(C *c) : ClonePolicy<C>(c) {} |
| |
| private: |
| std::map<const void *, void *> map; |
| |
| protected: |
| virtual void *lookup(void *obj) |
| { |
| return map[obj]; |
| } |
| |
| virtual void insert(const void *obj, void *clone) |
| { |
| map[obj] = clone; |
| } |
| }; |
| |
| template<typename S, typename T> |
| struct bimap |
| { |
| std::map<S, T> forth; |
| std::map<T, S> back; |
| |
| public: |
| bimap() : l(back), r(forth) { } |
| bimap(const bimap<S, T> &m) |
| : forth(m.forth), back(m.back), l(back), r(forth) { } |
| |
| void insert(const S &s, const T &t) |
| { |
| forth.insert(std::make_pair(s, t)); |
| back.insert(std::make_pair(t, s)); |
| } |
| |
| typedef typename std::map<T, S>::const_iterator l_iterator; |
| const std::map<T, S> &l; |
| typedef typename std::map<S, T>::const_iterator r_iterator; |
| const std::map<S, T> &r; |
| }; |
| |
| } // namespace nv50_ir |
| |
| #endif // __NV50_IR_UTIL_H__ |