From 832aae4894e53d3ee674e5d9df73dd8fc13e52b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 15 Oct 2017 22:13:49 +0200 Subject: [PATCH 1/2] Export DIV to C++, use llvm::APInt --- libevmjit/Arith256.cpp | 83 ++++++++++++------------------------------ libevmjit/JIT.cpp | 24 ++++++++++++ 2 files changed, 47 insertions(+), 60 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index bebb03bc..300b7639 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -45,72 +45,35 @@ llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char func->setDoesNotThrow(); func->setDoesNotAccessMemory(); - auto zero = llvm::ConstantInt::get(_type, 0); - auto one = llvm::ConstantInt::get(_type, 1); - auto iter = func->arg_begin(); llvm::Argument* x = &(*iter++); x->setName("x"); llvm::Argument* y = &(*iter); y->setName("y"); - auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func); - auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func); - auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func); - auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func); - auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func); - - auto builder = IRBuilder{entryBB}; - auto yLEx = builder.CreateICmpULE(y, x); - auto r0 = x; - builder.CreateCondBr(yLEx, mainBB, returnBB); - - builder.SetInsertPoint(mainBB); - auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); - // both y and r are non-zero - auto yLz = builder.CreateCall(ctlzIntr, {y, builder.getInt1(true)}, "y.lz"); - auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz"); - auto i0 = builder.CreateNUWSub(yLz, rLz, "i0"); - auto y0 = builder.CreateShl(y, i0); - builder.CreateBr(loopBB); - - builder.SetInsertPoint(loopBB); - auto yPhi = builder.CreatePHI(_type, 2, "y.phi"); - auto rPhi = builder.CreatePHI(_type, 2, "r.phi"); - auto iPhi = builder.CreatePHI(_type, 2, "i.phi"); - auto qPhi = builder.CreatePHI(_type, 2, "q.phi"); - auto rUpdate = builder.CreateNUWSub(rPhi, yPhi); - auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 - auto rGEy = builder.CreateICmpUGE(rPhi, yPhi); - auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); - auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); - auto iZero = builder.CreateICmpEQ(iPhi, zero); - builder.CreateCondBr(iZero, returnBB, continueBB); - - builder.SetInsertPoint(continueBB); - auto i2 = builder.CreateNUWSub(iPhi, one); - auto q2 = builder.CreateShl(q1, one); - auto y2 = builder.CreateLShr(yPhi, one); - builder.CreateBr(loopBB); - - yPhi->addIncoming(y0, mainBB); - yPhi->addIncoming(y2, continueBB); - rPhi->addIncoming(r0, mainBB); - rPhi->addIncoming(r1, continueBB); - iPhi->addIncoming(i0, mainBB); - iPhi->addIncoming(i2, continueBB); - qPhi->addIncoming(zero, mainBB); - qPhi->addIncoming(q2, continueBB); - - builder.SetInsertPoint(returnBB); - auto qRet = builder.CreatePHI(_type, 2, "q.ret"); - qRet->addIncoming(zero, entryBB); - qRet->addIncoming(q1, loopBB); - auto rRet = builder.CreatePHI(_type, 2, "r.ret"); - rRet->addIncoming(r0, entryBB); - rRet->addIncoming(r1, loopBB); - auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); - ret = builder.CreateInsertElement(ret, rRet, 1, "ret"); + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = IRBuilder{bb}; + auto allocaQ = builder.CreateAlloca(_type, nullptr, "alloca.q"); + auto allocaR = builder.CreateAlloca(_type, nullptr, "alloca.r"); + auto allocaN = builder.CreateAlloca(_type, nullptr, "alloca.n"); + auto allocaD = builder.CreateAlloca(_type, nullptr, "alloca.d"); + builder.CreateStore(x, allocaN); + builder.CreateStore(y, allocaD); + + auto ptrTy = _type->getPointerTo(); + + auto extFunc = llvm::Function::Create( + llvm::FunctionType::get(builder.getVoidTy(), {ptrTy, ptrTy, ptrTy, ptrTy}, false), + llvm::Function::ExternalLinkage, + {"external.", llvm::StringRef{_funcName}}, + &_module + ); + + builder.CreateCall(extFunc, {allocaQ, allocaR, allocaN, allocaD}); + auto q = builder.CreateLoad(allocaQ); + auto r = builder.CreateLoad(allocaR); + auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0), "ret0"); + ret = builder.CreateInsertElement(ret, r, 1, "ret"); builder.CreateRet(ret); return func; diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index 6091820b..7be3fe01 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -216,6 +216,28 @@ int64_t call_v2( return r; } +template +void div(uint64_t q_words[], uint64_t r_words[], const uint64_t n_words[], const uint64_t d_words[]) +{ + constexpr unsigned numWords = NumBits / 8 / sizeof(uint64_t); + static_assert(numWords * 8 * sizeof(uint64_t) == NumBits, "Invalid NumBits value"); + assert(llvm::APInt::getNumWords(NumBits) == numWords); + + llvm::APInt n{NumBits, {n_words, numWords}}; + llvm::APInt d{NumBits, {d_words, numWords}}; + + llvm::APInt q{NumBits, 0}; + llvm::APInt r{NumBits, 0}; + + if (d != 0) + llvm::APInt::udivrem(n, d, q, r); + + std::copy(q.getRawData(), q.getRawData() + q.getActiveWords(), q_words); + std::fill_n(q_words + q.getActiveWords(), numWords - q.getActiveWords(), 0); + std::copy(r.getRawData(), r.getRawData() + r.getActiveWords(), r_words); + std::fill_n(r_words + r.getActiveWords(), numWords - r.getActiveWords(), 0); +} + class SymbolResolver : public llvm::SectionMemoryManager { @@ -242,6 +264,8 @@ class SymbolResolver : public llvm::SectionMemoryManager .Case("evm.get_tx_context", reinterpret_cast(jit.host->get_tx_context)) .Case("evm.blockhash", reinterpret_cast(jit.host->get_block_hash)) .Case("evm.log", reinterpret_cast(jit.host->log)) + .Case("external.evm.udivrem.i256", reinterpret_cast(div<256>)) + .Case("external.evm.udivrem.i512", reinterpret_cast(div<512>)) .Default(0); if (addr) return {addr, llvm::JITSymbolFlags::Exported}; From 19d13137db43fd71c459ecd5a7567d4a094d0f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 19 Oct 2017 15:00:01 +0200 Subject: [PATCH 2/2] div: Use low level llvm::APInt::tcDivide() This is actually slower on average because it does not eliminate leading zeros. --- libevmjit/JIT.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index 7be3fe01..8c628c9f 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -217,25 +217,21 @@ int64_t call_v2( } template -void div(uint64_t q_words[], uint64_t r_words[], const uint64_t n_words[], const uint64_t d_words[]) +void div(uint64_t quotient[], uint64_t reminder[], const uint64_t numerator[], const uint64_t divisor[]) { constexpr unsigned numWords = NumBits / 8 / sizeof(uint64_t); static_assert(numWords * 8 * sizeof(uint64_t) == NumBits, "Invalid NumBits value"); - assert(llvm::APInt::getNumWords(NumBits) == numWords); - llvm::APInt n{NumBits, {n_words, numWords}}; - llvm::APInt d{NumBits, {d_words, numWords}}; + std::array scratch; // NOLINT: Allowed to be uninitialized. + std::copy_n(numerator, numWords, quotient); - llvm::APInt q{NumBits, 0}; - llvm::APInt r{NumBits, 0}; + int status = llvm::APInt::tcDivide(quotient, divisor, reminder, scratch.data(), numWords); - if (d != 0) - llvm::APInt::udivrem(n, d, q, r); - - std::copy(q.getRawData(), q.getRawData() + q.getActiveWords(), q_words); - std::fill_n(q_words + q.getActiveWords(), numWords - q.getActiveWords(), 0); - std::copy(r.getRawData(), r.getRawData() + r.getActiveWords(), r_words); - std::fill_n(r_words + r.getActiveWords(), numWords - r.getActiveWords(), 0); + if (status == 1) // Division by 0. + { + std::fill_n(quotient, numWords, 0); + std::fill_n(reminder, numWords, 0); + } }