mirror of
https://github.com/herumi/xbyak
synced 2024-11-21 16:09:11 -07:00
support auto_grow and user allocator
This commit is contained in:
parent
34febce30b
commit
3669c8844d
4 changed files with 183 additions and 31 deletions
52
test/jmp.cpp
52
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
0
test/test_nm.sh
Executable file → Normal file
0
test/test_nm.sh
Executable file → Normal file
160
xbyak/xbyak.h
160
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 <stdio.h> // for debug print
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#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<uint8*>(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<uint8*>(alloc_->alloc(maxSize + ALIGN_PAGE_SIZE)) : 0)
|
||||
, maxSize_(maxSize)
|
||||
, top_(type_ == ALLOC_BUF ? getAlignedAddress(allocPtr_, ALIGN_PAGE_SIZE) : type_ == USER_BUF ? reinterpret_cast<uint8*>(userPtr) : buf_)
|
||||
, top_(isAllocType() ? getAlignedAddress(allocPtr_, ALIGN_PAGE_SIZE) : type_ == USER_BUF ? reinterpret_cast<uint8*>(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<uint8>(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<void*>(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<void*>(addr), size, mode, &oldProtect) != 0;
|
||||
#elif defined(__GNUC__)
|
||||
size_t pageSize = sysconf(_SC_PAGESIZE);
|
||||
size_t iaddr = reinterpret_cast<size_t>(addr);
|
||||
size_t roundAddr = iaddr & ~(pageSize - static_cast<size_t>(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<void*>(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<AddrInfo> 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<uint8*>(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<const uint8*>(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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue