You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
320 lines
11 KiB
320 lines
11 KiB
#include "Array.h"
|
|
|
|
#include "preprocessor/llvm_includes_start.h"
|
|
#include <llvm/IR/Module.h>
|
|
#include <llvm/IR/Function.h>
|
|
#include "preprocessor/llvm_includes_end.h"
|
|
|
|
#include "RuntimeManager.h"
|
|
#include "Runtime.h"
|
|
#include "Utils.h"
|
|
|
|
#include <set> // DEBUG only
|
|
|
|
namespace dev
|
|
{
|
|
namespace eth
|
|
{
|
|
namespace jit
|
|
{
|
|
|
|
static const auto c_reallocStep = 1;
|
|
static const auto c_reallocMultipier = 2;
|
|
|
|
llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name)
|
|
{
|
|
if (!m_func)
|
|
m_func = m_creator();
|
|
|
|
return _builder.CreateCall(m_func, {_args.begin(), _args.size()}, _name);
|
|
}
|
|
|
|
llvm::Function* Array::createArrayPushFunc()
|
|
{
|
|
llvm::Type* argTypes[] = {m_array->getType(), Type::Word};
|
|
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.push", getModule());
|
|
func->setDoesNotThrow();
|
|
func->setDoesNotCapture(1);
|
|
|
|
auto arrayPtr = &func->getArgumentList().front();
|
|
arrayPtr->setName("arrayPtr");
|
|
auto value = arrayPtr->getNextNode();
|
|
value->setName("value");
|
|
|
|
InsertPointGuard guard{m_builder};
|
|
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func);
|
|
auto reallocBB = llvm::BasicBlock::Create(m_builder.getContext(), "Realloc", func);
|
|
auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func);
|
|
|
|
m_builder.SetInsertPoint(entryBB);
|
|
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr");
|
|
auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr");
|
|
auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr");
|
|
auto data = m_builder.CreateLoad(dataPtr, "data");
|
|
auto size = m_builder.CreateLoad(sizePtr, "size");
|
|
auto cap = m_builder.CreateLoad(capPtr, "cap");
|
|
auto reallocReq = m_builder.CreateICmpEQ(cap, size, "reallocReq");
|
|
m_builder.CreateCondBr(reallocReq, reallocBB, pushBB);
|
|
|
|
m_builder.SetInsertPoint(reallocBB);
|
|
auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap");
|
|
//newCap = m_builder.CreateNUWMul(newCap, m_builder.getInt64(c_reallocMultipier));
|
|
auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32
|
|
auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes");
|
|
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes");
|
|
auto newData = m_builder.CreateBitCast(newBytes, Type::WordPtr, "newData");
|
|
m_builder.CreateStore(newData, dataPtr);
|
|
m_builder.CreateStore(newCap, capPtr);
|
|
m_builder.CreateBr(pushBB);
|
|
|
|
m_builder.SetInsertPoint(pushBB);
|
|
auto dataPhi = m_builder.CreatePHI(Type::WordPtr, 2, "dataPhi");
|
|
dataPhi->addIncoming(data, entryBB);
|
|
dataPhi->addIncoming(newData, reallocBB);
|
|
auto newElemPtr = m_builder.CreateGEP(dataPhi, size, "newElemPtr");
|
|
m_builder.CreateStore(value, newElemPtr);
|
|
auto newSize = m_builder.CreateNUWAdd(size, m_builder.getInt64(1), "newSize");
|
|
m_builder.CreateStore(newSize, sizePtr);
|
|
m_builder.CreateRetVoid();
|
|
|
|
return func;
|
|
}
|
|
|
|
llvm::Function* Array::createArraySetFunc()
|
|
{
|
|
llvm::Type* argTypes[] = {m_array->getType(), Type::Size, Type::Word};
|
|
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.set", getModule());
|
|
func->setDoesNotThrow();
|
|
func->setDoesNotCapture(1);
|
|
|
|
auto arrayPtr = &func->getArgumentList().front();
|
|
arrayPtr->setName("arrayPtr");
|
|
auto index = arrayPtr->getNextNode();
|
|
index->setName("index");
|
|
auto value = index->getNextNode();
|
|
value->setName("value");
|
|
|
|
InsertPointGuard guard{m_builder};
|
|
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
|
|
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr");
|
|
auto data = m_builder.CreateLoad(dataPtr, "data");
|
|
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr");
|
|
m_builder.CreateStore(value, valuePtr);
|
|
m_builder.CreateRetVoid();
|
|
return func;
|
|
}
|
|
|
|
llvm::Function* Array::createArrayGetFunc()
|
|
{
|
|
llvm::Type* argTypes[] = {m_array->getType(), Type::Size};
|
|
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "array.get", getModule());
|
|
func->setDoesNotThrow();
|
|
func->setDoesNotCapture(1);
|
|
|
|
auto arrayPtr = &func->getArgumentList().front();
|
|
arrayPtr->setName("arrayPtr");
|
|
auto index = arrayPtr->getNextNode();
|
|
index->setName("index");
|
|
|
|
InsertPointGuard guard{m_builder};
|
|
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
|
|
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr");
|
|
auto data = m_builder.CreateLoad(dataPtr, "data");
|
|
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr");
|
|
auto value = m_builder.CreateLoad(valuePtr, "value");
|
|
m_builder.CreateRet(value);
|
|
return func;
|
|
}
|
|
|
|
llvm::Function* Array::createGetPtrFunc()
|
|
{
|
|
llvm::Type* argTypes[] = {m_array->getType(), Type::Size};
|
|
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::PrivateLinkage, "array.getPtr", getModule());
|
|
func->setDoesNotThrow();
|
|
func->setDoesNotCapture(1);
|
|
|
|
auto arrayPtr = &func->getArgumentList().front();
|
|
arrayPtr->setName("arrayPtr");
|
|
auto index = arrayPtr->getNextNode();
|
|
index->setName("index");
|
|
|
|
InsertPointGuard guard{m_builder};
|
|
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
|
|
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");
|
|
auto data = m_builder.CreateLoad(dataPtr, "data");
|
|
auto bytePtr = m_builder.CreateGEP(data, index, "bytePtr");
|
|
auto wordPtr = m_builder.CreateBitCast(bytePtr, Type::WordPtr, "wordPtr");
|
|
m_builder.CreateRet(wordPtr);
|
|
return func;
|
|
}
|
|
|
|
llvm::Function* Array::createFreeFunc()
|
|
{
|
|
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, m_array->getType(), false), llvm::Function::PrivateLinkage, "array.free", getModule());
|
|
func->setDoesNotThrow();
|
|
func->setDoesNotCapture(1);
|
|
|
|
auto freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::BytePtr, false), llvm::Function::ExternalLinkage, "ext_free", getModule());
|
|
freeFunc->setDoesNotThrow();
|
|
freeFunc->setDoesNotCapture(1);
|
|
|
|
auto arrayPtr = &func->getArgumentList().front();
|
|
arrayPtr->setName("arrayPtr");
|
|
|
|
InsertPointGuard guard{m_builder};
|
|
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
|
|
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr");
|
|
auto data = m_builder.CreateLoad(dataPtr, "data");
|
|
auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem");
|
|
m_builder.CreateCall(freeFunc, mem);
|
|
m_builder.CreateRetVoid();
|
|
return func;
|
|
}
|
|
|
|
llvm::Function* Array::getReallocFunc()
|
|
{
|
|
if (auto func = getModule()->getFunction("ext_realloc"))
|
|
return func;
|
|
|
|
llvm::Type* reallocArgTypes[] = {Type::BytePtr, Type::Size};
|
|
auto reallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, reallocArgTypes, false), llvm::Function::ExternalLinkage, "ext_realloc", getModule());
|
|
reallocFunc->setDoesNotThrow();
|
|
reallocFunc->setDoesNotAlias(0);
|
|
reallocFunc->setDoesNotCapture(1);
|
|
return reallocFunc;
|
|
}
|
|
|
|
llvm::Function* Array::createExtendFunc()
|
|
{
|
|
llvm::Type* argTypes[] = {m_array->getType(), Type::Size};
|
|
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.extend", getModule());
|
|
func->setDoesNotThrow();
|
|
func->setDoesNotCapture(1);
|
|
|
|
auto arrayPtr = &func->getArgumentList().front();
|
|
arrayPtr->setName("arrayPtr");
|
|
auto newSize = arrayPtr->getNextNode();
|
|
newSize->setName("newSize");
|
|
|
|
InsertPointGuard guard{m_builder};
|
|
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
|
|
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array
|
|
auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr");
|
|
auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr");
|
|
auto data = m_builder.CreateLoad(dataPtr, "data");
|
|
auto size = m_builder.CreateLoad(sizePtr, "size");
|
|
auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize");
|
|
auto newData = m_reallocFunc.call(m_builder, {data, newSize}, "newData"); // TODO: Check realloc result for null
|
|
auto extPtr = m_builder.CreateGEP(newData, size, "extPtr");
|
|
m_builder.CreateMemSet(extPtr, m_builder.getInt8(0), extSize, 16);
|
|
m_builder.CreateStore(newData, dataPtr);
|
|
m_builder.CreateStore(newSize, sizePtr);
|
|
m_builder.CreateStore(newSize, capPtr);
|
|
m_builder.CreateRetVoid();
|
|
return func;
|
|
}
|
|
|
|
llvm::Type* Array::getType()
|
|
{
|
|
llvm::Type* elementTys[] = {Type::WordPtr, Type::Size, Type::Size};
|
|
static auto arrayTy = llvm::StructType::create(elementTys, "Array");
|
|
return arrayTy;
|
|
}
|
|
|
|
Array::Array(llvm::IRBuilder<>& _builder, char const* _name) :
|
|
CompilerHelper(_builder),
|
|
m_pushFunc([this](){ return createArrayPushFunc(); }),
|
|
m_setFunc([this](){ return createArraySetFunc(); }),
|
|
m_getFunc([this](){ return createArrayGetFunc(); }),
|
|
m_freeFunc([this](){ return createFreeFunc(); })
|
|
{
|
|
m_array = m_builder.CreateAlloca(getType(), nullptr, _name);
|
|
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array);
|
|
}
|
|
|
|
Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) :
|
|
CompilerHelper(_builder),
|
|
m_array(_array),
|
|
m_pushFunc([this](){ return createArrayPushFunc(); }),
|
|
m_setFunc([this](){ return createArraySetFunc(); }),
|
|
m_getFunc([this](){ return createArrayGetFunc(); }),
|
|
m_freeFunc([this](){ return createFreeFunc(); })
|
|
{
|
|
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array);
|
|
}
|
|
|
|
|
|
void Array::pop(llvm::Value* _count)
|
|
{
|
|
auto sizePtr = m_builder.CreateStructGEP(m_array, 1, "sizePtr");
|
|
auto size = m_builder.CreateLoad(sizePtr, "size");
|
|
auto newSize = m_builder.CreateNUWSub(size, _count, "newSize");
|
|
m_builder.CreateStore(newSize, sizePtr);
|
|
}
|
|
|
|
llvm::Value* Array::size(llvm::Value* _array)
|
|
{
|
|
auto sizePtr = m_builder.CreateStructGEP(_array ? _array : m_array, 1, "sizePtr");
|
|
return m_builder.CreateLoad(sizePtr, "array.size");
|
|
}
|
|
|
|
void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size)
|
|
{
|
|
assert(_arrayPtr->getType() == m_array->getType());
|
|
assert(_size->getType() == Type::Size);
|
|
m_extendFunc.call(m_builder, {_arrayPtr, _size});
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
struct AllocatedMemoryWatchdog
|
|
{
|
|
std::set<void*> allocatedMemory;
|
|
|
|
~AllocatedMemoryWatchdog()
|
|
{
|
|
if (!allocatedMemory.empty())
|
|
{
|
|
DLOG(mem) << allocatedMemory.size() << " MEM LEAKS!\n";
|
|
for (auto&& leak : allocatedMemory)
|
|
DLOG(mem) << "\t" << leak << "\n";
|
|
}
|
|
}
|
|
};
|
|
|
|
AllocatedMemoryWatchdog watchdog;
|
|
}
|
|
|
|
extern "C"
|
|
{
|
|
using namespace dev::eth::jit;
|
|
|
|
EXPORT void* ext_realloc(void* _data, size_t _size) noexcept
|
|
{
|
|
//std::cerr << "REALLOC: " << _data << " [" << _size << "]" << std::endl;
|
|
auto newData = std::realloc(_data, _size);
|
|
if (_data != newData)
|
|
{
|
|
DLOG(mem) << "REALLOC: " << newData << " <- " << _data << " [" << _size << "]\n";
|
|
watchdog.allocatedMemory.erase(_data);
|
|
watchdog.allocatedMemory.insert(newData);
|
|
}
|
|
return newData;
|
|
}
|
|
|
|
EXPORT void ext_free(void* _data) noexcept
|
|
{
|
|
std::free(_data);
|
|
if (_data)
|
|
{
|
|
DLOG(mem) << "FREE : " << _data << "\n";
|
|
watchdog.allocatedMemory.erase(_data);
|
|
}
|
|
}
|
|
|
|
} // extern "C"
|
|
|
|
|