From 3669c8844dd70d17d3a4ef6bce0975afc7e45cf6 Mon Sep 17 00:00:00 2001 From: MITSUNARI Shigeo Date: Fri, 16 Mar 2012 15:47:23 +0900 Subject: [PATCH] support auto_grow and user allocator --- test/jmp.cpp | 52 ++++++++++++++- test/test_jmp.bat | 2 +- test/test_nm.sh | 0 xbyak/xbyak.h | 160 +++++++++++++++++++++++++++++++++++++--------- 4 files changed, 183 insertions(+), 31 deletions(-) mode change 100755 => 100644 test/test_nm.sh diff --git a/test/jmp.cpp b/test/jmp.cpp index 81a6a18..f53face 100644 --- a/test/jmp.cpp +++ b/test/jmp.cpp @@ -66,7 +66,7 @@ struct TestJmp : public CodeGenerator { } }; -int main() +void test1() { static const struct Tbl { int offset; @@ -99,3 +99,53 @@ int main() } } } + +int add5(int x) { return x + 5; } +int add2(int x) { return x + 2; } + +struct Grow : Xbyak::CodeGenerator { + Grow(int dummySize) + : Xbyak::CodeGenerator(128, Xbyak::AutoGrow) + { + mov(eax, 100); + push(eax); + call(add5); + add(esp, 4); + push(eax); + call(add2); + add(esp, 4); + ret(); + for (int i = 0; i < dummySize; i++) { + db(0); + } + } +}; + +void test2() +{ + for (int dummySize = 0; dummySize < 40000; dummySize += 10000) { + printf("dummySize=%d\n", dummySize); + Grow g(dummySize); + g.ready(); + int (*f)() = (int (*)())g.getCode(); + int x = f(); + const int ok = 107; + if (x != ok) { + printf("err %d assume %d\n", x, ok); + } else { + printf("ok\n"); + } + } +} + +int main() +{ + try { + test1(); + test2(); + } catch (Xbyak::Error err) { + printf("ERR:%s(%d)\n", Xbyak::ConvertErrorToString(err), err); + } catch (...) { + printf("unknown error\n"); + } +} diff --git a/test/test_jmp.bat b/test/test_jmp.bat index eb333d6..cb4fe09 100644 --- a/test/test_jmp.bat +++ b/test/test_jmp.bat @@ -1,5 +1,5 @@ pushd ..\gen call update popd -cl -I../ -DXBYAK_TEST jmp.cpp %OPT% +cl -I../ -DXBYAK_TEST jmp.cpp %OPT% /Od /Zi jmp diff --git a/test/test_nm.sh b/test/test_nm.sh old mode 100755 new mode 100644 diff --git a/xbyak/xbyak.h b/xbyak/xbyak.h index e7d795d..81f45f3 100644 --- a/xbyak/xbyak.h +++ b/xbyak/xbyak.h @@ -5,9 +5,9 @@ @file xbyak.h @brief Xbyak ; JIT assembler for x86(IA32)/x64 by C++ @author herumi - @version $Revision: 1.260 $ + @version $Revision: 1.267 $ @url http://homepage1.nifty.com/herumi/soft/xbyak.html - @date $Date: 2012/01/05 00:34:07 $ + @date $Date: 2012/03/16 06:21:16 $ @note modified new BSD license http://opensource.org/licenses/BSD-3-Clause */ @@ -18,6 +18,7 @@ #include // for debug print #include #include +#include #include #include #ifdef _WIN32 @@ -105,6 +106,9 @@ enum Error { ERR_BAD_ST_COMBINATION, ERR_OVER_LOCAL_LABEL, ERR_UNDER_LOCAL_LABEL, + ERR_CANT_ALLOC, + ERR_ONLY_T_NEAR_IS_SUPPORTED_IN_AUTO_GROW, + ERR_BAD_PROTECT_MODE, ERR_INTERNAL }; @@ -133,12 +137,24 @@ static inline const char *ConvertErrorToString(Error err) "bad st combination", "over local label", "under local label", + "can't alloc", + "T_SHORT is not supported in AutoGrow", + "bad protect mode", "internal error", }; if (err < 0 || err > ERR_INTERNAL) return 0; return errTbl[err]; } +/* + custom allocator +*/ +struct Allocator { + virtual uint8 *alloc(size_t size) { return new uint8[size]; } + virtual void free(uint8 *p) { delete[] p; } + virtual ~Allocator() {} +}; + namespace inner { enum { debug = 1 }; @@ -390,45 +406,76 @@ struct RegRip { }; #endif +// 2nd parameter for constructor of CodeArray(maxSize, userPtr, alloc) +void *const AutoGrow = (void*)1; + class CodeArray { enum { ALIGN_PAGE_SIZE = 4096, MAX_FIXED_BUF_SIZE = 8 }; + void operator=(const CodeArray&); + void resize(size_t newSize) + { + uint8 *newAllocPtr = reinterpret_cast(alloc_->alloc(newSize + ALIGN_PAGE_SIZE)); + if (newAllocPtr == 0) throw ERR_CANT_ALLOC; + uint8 *newTop = getAlignedAddress(newAllocPtr, ALIGN_PAGE_SIZE); + for (size_t i = 0; i < size_; i++) newTop[i] = top_[i]; + alloc_->free(allocPtr_); + allocPtr_ = newAllocPtr; + top_ = newTop; + maxSize_ = newSize; + } +protected: enum Type { FIXED_BUF, // use buf_(non alignment, non protect) USER_BUF, // use userPtr(non alignment, non protect) - ALLOC_BUF // use new(alignment, protect) + ALLOC_BUF, // use new(alignment, protect) + AUTO_GROW // automatically move and grow memory if necessary }; - void operator=(const CodeArray&); - Type type_; - uint8 *const allocPtr_; // for ALLOC_BUF + bool isAutoGrow() const { return type_ == AUTO_GROW; } +private: + bool isAllocType() const { return type_ == ALLOC_BUF || type_ == AUTO_GROW; } + Type getType(size_t maxSize, void *userPtr) const + { + if (userPtr == AutoGrow) return AUTO_GROW; + if (maxSize <= MAX_FIXED_BUF_SIZE) return FIXED_BUF; + return ALLOC_BUF; + } + const Type type_; + Allocator defaultAllocator_; + Allocator *alloc_; + uint8 *allocPtr_; // for ALLOC_BUF uint8 buf_[MAX_FIXED_BUF_SIZE]; // for FIXED_BUF protected: - const size_t maxSize_; - uint8 *const top_; + size_t maxSize_; + uint8 *top_; size_t size_; public: - CodeArray(size_t maxSize = MAX_FIXED_BUF_SIZE, void *userPtr = 0) - : type_(userPtr ? USER_BUF : maxSize <= MAX_FIXED_BUF_SIZE ? FIXED_BUF : ALLOC_BUF) - , allocPtr_(type_ == ALLOC_BUF ? new uint8[maxSize + ALIGN_PAGE_SIZE] : 0) + CodeArray(size_t maxSize = MAX_FIXED_BUF_SIZE, void *userPtr = 0, Allocator *allocator = 0) + : type_(getType(maxSize, userPtr)) + , alloc_(allocator ? allocator : &defaultAllocator_) + , allocPtr_(isAllocType() ? reinterpret_cast(alloc_->alloc(maxSize + ALIGN_PAGE_SIZE)) : 0) , maxSize_(maxSize) - , top_(type_ == ALLOC_BUF ? getAlignedAddress(allocPtr_, ALIGN_PAGE_SIZE) : type_ == USER_BUF ? reinterpret_cast(userPtr) : buf_) + , top_(isAllocType() ? getAlignedAddress(allocPtr_, ALIGN_PAGE_SIZE) : type_ == USER_BUF ? reinterpret_cast(userPtr) : buf_) , size_(0) { - if (type_ == ALLOC_BUF && !protect(top_, maxSize, true)) { + if (maxSize_ > 0 && top_ == 0) throw ERR_CANT_ALLOC; + if (type_ == ALLOC_BUF && !protect(top_, maxSize, ReadWriteExecMode)) { + alloc_->free(allocPtr_); throw ERR_CANT_PROTECT; } } virtual ~CodeArray() { - if (type_ == ALLOC_BUF) { - protect(top_, maxSize_, false); - delete[] allocPtr_; + if (isAllocType()) { + protect(top_, maxSize_, ReadWriteMode); + alloc_->free(allocPtr_); } } CodeArray(const CodeArray& rhs) : type_(rhs.type_) + , defaultAllocator_(rhs.defaultAllocator_) , allocPtr_(0) , maxSize_(rhs.maxSize_) , top_(buf_) @@ -439,7 +486,13 @@ public: } void db(int code) { - if (size_ >= maxSize_) throw ERR_CODE_IS_TOO_BIG; + if (size_ >= maxSize_) { + if (type_ == AUTO_GROW) { + resize(maxSize_ + ALIGN_PAGE_SIZE); + } else { + throw ERR_CODE_IS_TOO_BIG; + } + } top_[size_++] = static_cast(code); } void db(const uint8 *code, int codeSize) @@ -498,19 +551,48 @@ public: change exec permission of memory @param addr [in] buffer address @param size [in] buffer size - @param canExec [in] true(enable to exec), false(disable to exec) + @param protectMode [in] 0:(write) 1:(write+exec) 2:(exec) @return true(success), false(failure) */ - static inline bool protect(const void *addr, size_t size, bool canExec) + static const int ReadWriteMode = 0; + static const int ReadWriteExecMode = 1; + static const int ReadExecMode = 2; + static inline bool protect(const void *addr, size_t size, int protectMode) { #if defined(_WIN32) - DWORD oldProtect; - return VirtualProtect(const_cast(addr), size, canExec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldProtect) != 0; + DWORD oldProtect, mode; + switch (protectMode) { + case ReadWriteMode: + mode = PAGE_READWRITE; + break; + case ReadWriteExecMode: + mode = PAGE_EXECUTE_READWRITE; + break; + case ReadExecMode: + mode = PAGE_EXECUTE_READ; + break; + default: + throw ERR_BAD_PROTECT_MODE; + } + return VirtualProtect(const_cast(addr), size, mode, &oldProtect) != 0; #elif defined(__GNUC__) size_t pageSize = sysconf(_SC_PAGESIZE); size_t iaddr = reinterpret_cast(addr); size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); - int mode = PROT_READ | PROT_WRITE | (canExec ? PROT_EXEC : 0); + int mode; + switch (protectMode) { + case ReadWriteMode: + mode = PROT_READ | PROT_WRITE; + break; + case ReadWriteExecMode: + mode = PROT_READ | PROT_WRITE | PROT_EXEC; + break; + case ReadExecMode: + mode = PROT_READ | PROT_EXEC; + break; + default: + throw ERR_BAD_PROTECT_MODE; + } return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; #else return true; @@ -873,25 +955,35 @@ private: label_.addUndefinedLabel(label, jmp); } } + struct AddrInfo { + size_t offset_; + const uint8 *addr_; + AddrInfo(size_t offset, const uint8 *addr) : offset_(offset), addr_(addr) {} + }; + typedef std::list AddrInfoList; + AddrInfoList addrInfoList_; void opJmp(const void *addr, LabelType type, uint8 shortCode, uint8 longCode, uint8 longPref) { + if (isAutoGrow() && type != T_NEAR) throw ERR_ONLY_T_NEAR_IS_SUPPORTED_IN_AUTO_GROW; const int shortHeaderSize = 1; const int shortJmpSize = shortHeaderSize + 1; /* +1 means 8-bit displacement */ const int longHeaderSize = longPref ? 2 : 1; const int longJmpSize = longHeaderSize + 4; /* +4 means 32-bit displacement */ - uint8 *top = const_cast(getCurr()); - uint32 disp = inner::GetPtrDist(addr, top); + uint32 disp = inner::GetPtrDist(addr, getCurr()); if (type != T_NEAR && inner::IsInDisp8(disp - shortJmpSize)) { db(shortCode); - db(0); - rewrite(top + shortHeaderSize, disp - shortJmpSize, 1); + db(disp - shortJmpSize); } else { if (type == T_SHORT) throw ERR_LABEL_IS_TOO_FAR; if (longPref) db(longPref); db(longCode); - dd(0); - rewrite(top + longHeaderSize, disp - longJmpSize, 4); + if (isAutoGrow()) { + addrInfoList_.push_back(AddrInfo(size_, reinterpret_cast(addr) - longJmpSize + 1)); + dd(0); + } else { + dd(disp - longJmpSize); + } } } /* preCode is for SSSE3/SSE4 */ @@ -1483,9 +1575,19 @@ public: const uint8 *getCode() const { assert(!hasUndefinedLabel()); -// if (hasUndefinedLabel()) throw ERR_LABEL_IS_NOT_FOUND; return top_; } + /* + call ready() to complete generating code on AutoGrow + */ + void ready() + { + if (hasUndefinedLabel()) throw ERR_LABEL_IS_NOT_FOUND; + for (AddrInfoList::const_iterator i = addrInfoList_.begin(), ie = addrInfoList_.end(); i != ie; ++i) { + rewrite(top_ + i->offset_, (uint32)(i->addr_ - (top_ + i->offset_)), 4); + } + if (!protect(top_, size_, ReadWriteExecMode)) throw ERR_CANT_PROTECT; + } #ifdef XBYAK_TEST void dump(bool doClear = true) {