diff --git a/include/nil/blueprint/memory.hpp b/include/nil/blueprint/memory.hpp index 1d78e08b..578766fc 100644 --- a/include/nil/blueprint/memory.hpp +++ b/include/nil/blueprint/memory.hpp @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -47,13 +48,17 @@ namespace nil { template struct program_memory : public std::vector> { public: - program_memory(long stack_size) : heap_top(stack_size) { + program_memory(long stack_size) : stack_size(stack_size), heap_top(stack_size) { + // We cell's offset is (actual_offset + size), so we add initial cell with zero offset + // to easily compute a size of a cell as a difference with the previous one this->push_back({VarType(), 0}); this->resize(heap_top); push_frame(); } void stack_push(unsigned offset) { - this->operator[](stack_top++) = cell {VarType(), offset, 1}; + cell &new_cell = this->operator[](stack_top++); + new_cell.offset = offset; + new_cell.head = 1; } void push_frame() { @@ -85,14 +90,38 @@ namespace nil { } void store(ptr_type ptr, VarType value) { - this->operator[](ptr).v = value; + (*this)[ptr].v = value; } VarType load(ptr_type ptr) { - return this->operator[](ptr).v; + return (*this)[ptr].v; + } + + size_t ptrtoint(ptr_type ptr) { + // Actual offset is stored in the previous cell + return (*this)[ptr - 1].offset; + } + + ptr_type inttoptr(size_t offset) { + // Find the corresponding cell using binary search + auto left = this->begin(); + auto right = this->end(); + if (offset < stack_size) { + right = left + stack_top; + } else { + left = left + stack_size; + } + auto res = std::lower_bound( + left, right, offset, [](const cell &cell, size_t offset) { return cell.offset < offset; }); + if (res == right) { + return 0; + } + // The operation is inverse to ptrtoint, so we need to add 1 to get the desired ptr + return res - left + 1; } private: ptr_type stack_top = 1; + size_t stack_size; size_t heap_top; std::stack frames; }; diff --git a/include/nil/blueprint/parser.hpp b/include/nil/blueprint/parser.hpp index e3423753..13eecc35 100644 --- a/include/nil/blueprint/parser.hpp +++ b/include/nil/blueprint/parser.hpp @@ -180,6 +180,11 @@ namespace nil { template NumberType resolve_number(stack_frame &frame, const llvm::Value *value) { var scalar = frame.scalars[value]; + return resolve_number(scalar); + } + + template + NumberType resolve_number(var scalar) { auto scalar_value = var_value(assignmnt, scalar); NumberType number = (NumberType)static_cast(scalar_value.data); return number; @@ -217,9 +222,7 @@ namespace nil { llvm::Type *type = constant->getType(); if (type->isPointerTy()) { ASSERT_MSG(constant->isZeroValue(), "Only zero initializers are supported for pointers"); - // TODO: single zero - assignmnt.public_input(0, public_input_idx) = 0; - stack_memory.store(ptr++, var(0, public_input_idx++, false, var::column_type::public_input)); + stack_memory.store(ptr++, zero_var); continue; } if (!type->isAggregateType() && !type->isVectorTy()) { @@ -367,34 +370,95 @@ namespace nil { } - ptr_type handle_gep(const llvm::GetElementPtrInst* gep, stack_frame &frame) { - // Collect GEP indices - std::vector gep_indices; - for (unsigned i = 0; i < gep->getNumIndices(); ++i) { - var idx_var = frame.scalars[gep->getOperand(i + 1)]; - auto idx_vv = var_value(assignmnt, idx_var); - int gep_index = (int)static_cast(idx_vv.data); - gep_indices.push_back(gep_index); - } + typename BlueprintFieldType::value_type handle_gep(const llvm::GetElementPtrInst* gep, stack_frame &frame) { llvm::Type *gep_ty = gep->getSourceElementType(); - ptr_type ptr = resolve_number(frame, gep->getPointerOperand()); + typename BlueprintFieldType::value_type ptr = + var_value(assignmnt, frame.scalars[gep->getPointerOperand()]); - int initial_ptr_adjustment = layout_resolver->get_type_layout(gep_ty).size() * gep_indices[0]; + var gep_initial_idx = frame.scalars[gep->getOperand(1)]; + size_t cells_for_type = layout_resolver->get_type_layout(gep_ty).size(); + auto initial_ptr_adjustment = cells_for_type * var_value(assignmnt, gep_initial_idx); ptr += initial_ptr_adjustment; - gep_indices.erase(gep_indices.begin()); - if (!gep_indices.empty()) { + if (gep->getNumIndices() > 1) { if (!gep_ty->isAggregateType()) { std::cerr << "GEP instruction with > 1 indices must operate on aggregate type!" << std::endl; return 0; } - int resolved_index = layout_resolver->get_flat_index(gep_ty, gep_indices); + // Collect GEP indices + std::vector gep_indices; + for (unsigned i = 1; i < gep->getNumIndices(); ++i) { + int gep_index = resolve_number(frame, gep->getOperand(i + 1)); + gep_indices.push_back(gep_index); + } + auto resolved_index = layout_resolver->get_flat_index(gep_ty, gep_indices); ptr += resolved_index; } return ptr; } + void handle_ptrtoint(const llvm::Value *inst, llvm::Value *operand, stack_frame &frame) { + ptr_type ptr = resolve_number(frame, operand); + size_t offset = stack_memory.ptrtoint(ptr); + log.debug("PtrToInt {} {}", ptr, offset); + assignmnt.public_input(0, public_input_idx) = offset; + frame.scalars[inst] = var(0, public_input_idx++, false, var::column_type::public_input); + } + + void put_constant(llvm::Constant *c, stack_frame &frame) { + if (llvm::isa(c) || llvm::isa(c)) { + assignmnt.public_input(0, public_input_idx) = marshal_int_val(c); + frame.scalars[c] = var(0, public_input_idx++, false, var::column_type::public_input); + } else if (llvm::isa(c)) { + llvm::Type *undef_type = c->getType(); + if (undef_type->isIntegerTy() || undef_type->isFieldTy()) { + frame.scalars[c] = undef_var; + } else if (auto vector_type = llvm::dyn_cast(undef_type)) { + frame.vectors[c] = std::vector(vector_type->getNumElements(), undef_var); + } else { + ASSERT(undef_type->isAggregateType()); + auto layout = layout_resolver->get_type_layout(undef_type); + ptr_type ptr = stack_memory.add_cells(layout); + for (size_t i = 0; i < layout.size(); ++i) { + stack_memory.store(ptr+i, undef_var); + } + assignmnt.public_input(0, public_input_idx) = ptr; + frame.scalars[c] = var(0, public_input_idx++, false, var::column_type::public_input); + } + } else if (llvm::isa(c)) { + frame.scalars[c] = zero_var; + } else if (auto *cv = llvm::dyn_cast(c)) { + size_t size = cv->getType()->getNumElements(); + std::vector result_vector(size); + for (int i = 0; i < size; ++i) { + llvm::Constant *elem = cv->getAggregateElement(i); + if (llvm::isa(elem)) + continue; + assignmnt.public_input(0, public_input_idx) = marshal_int_val(elem); + result_vector[i] = var(0, public_input_idx++, false, var::column_type::public_input); + } + frame.vectors[c] = result_vector; + } else if (auto expr = llvm::dyn_cast(c)) { + for (int i = 0; i < expr->getNumOperands(); ++i) { + put_constant(expr->getOperand(i), frame); + } + switch (expr->getOpcode()) { + case llvm::Instruction::PtrToInt: + handle_ptrtoint(expr, expr->getOperand(0), frame); + break; + default: + UNREACHABLE(std::string("Unhandled constant expression: ") + expr->getOpcodeName()); + } + } else if (auto addr = llvm::dyn_cast(c)) { + frame.scalars[c] = labels[addr->getBasicBlock()]; + } else { + // The only other known constant is an address of a function in CallInst, + // but there is no way to distinguish it + ASSERT(c->getType()->isPointerTy()); + } + } + const llvm::Instruction *handle_instruction(const llvm::Instruction *inst) { log.log_instruction(inst); stack_frame &frame = call_stack.top(); @@ -407,31 +471,10 @@ namespace nil { if (variables.find(op) != variables.end()) { continue; } - if (llvm::isa(op) || llvm::isa(op)) { - assignmnt.public_input(0, public_input_idx) = marshal_int_val(op); - variables[op] = var(0, public_input_idx++, false, var::column_type::public_input); - } - if (llvm::isa(op)) { - llvm::Type *undef_type = op->getType(); - if (undef_type->isIntegerTy() || undef_type->isFieldTy()) { - frame.scalars[op] = undef_var; - } else if (auto vector_type = llvm::dyn_cast(undef_type)) { - frame.vectors[op] = std::vector(vector_type->getNumElements(), undef_var); - } else { - ASSERT(undef_type->isAggregateType()); - auto layout = layout_resolver->get_type_layout(undef_type); - ptr_type ptr = stack_memory.add_cells(layout); - for (size_t i = 0; i < layout.size(); ++i) { - stack_memory.store(ptr+i, undef_var); - } - assignmnt.public_input(0, public_input_idx) = ptr; - frame.scalars[op] = var(0, public_input_idx++, false, var::column_type::public_input); - } - } else if (llvm::isa(op)) { - assignmnt.public_input(0, public_input_idx) = 0; - frame.scalars[op] = var(0, public_input_idx++, false, var::column_type::public_input); - } else if (llvm::isa(op)) { + if (llvm::isa(op)) { frame.scalars[op] = globals[op]; + } else if (llvm::isa(op)) { + put_constant(llvm::cast(op), frame); } } @@ -730,26 +773,7 @@ namespace nil { } int index = llvm::cast(index_value)->getZExtValue(); - std::vector result_vector; - if (llvm::isa(vec)) { - auto *vector_type = llvm::cast(vec->getType()); - ASSERT(vector_type->getElementType()->isFieldTy()); - unsigned size = vector_type->getNumElements(); - result_vector = std::vector(size); - if (auto *cv = llvm::dyn_cast(vec)) { - for (int i = 0; i < size; ++i) { - llvm::Constant *elem = cv->getAggregateElement(i); - if (llvm::isa(elem)) - continue; - assignmnt.public_input(0, public_input_idx) = marshal_int_val(elem); - result_vector[i] = var(0, public_input_idx++, false, var::column_type::public_input); - } - } else { - ASSERT_MSG(llvm::isa(vec), "Unexpected constant value!"); - } - } else { - result_vector = frame.vectors[vec]; - } + std::vector result_vector = frame.vectors[vec]; result_vector[index] = variables[inst->getOperand(1)]; frame.vectors[inst] = result_vector; return inst->getNextNonDebugInstruction(); @@ -778,12 +802,14 @@ namespace nil { } case llvm::Instruction::GetElementPtr: { auto *gep = llvm::cast(inst); - ptr_type gep_res = handle_gep(gep, frame); + auto gep_res = handle_gep(gep, frame); if (gep_res == 0) { std::cerr << "Incorrect GEP result!" << std::endl; return nullptr; } - log.debug("GEP: {}", gep_res); + std::ostringstream oss; + oss << gep_res.data; + log.debug("GEP: {}", oss.str()); assignmnt.public_input(0, public_input_idx) = gep_res; frame.scalars[gep] = var(0, public_input_idx++, false, var::column_type::public_input); return inst->getNextNonDebugInstruction(); @@ -798,7 +824,7 @@ namespace nil { case llvm::Instruction::Store: { auto *store_inst = llvm::cast(inst); ptr_type ptr = resolve_number(frame, store_inst->getPointerOperand()); - log.debug("Store ", ptr); + log.debug("Store: {}", ptr); const llvm::Value *val = store_inst->getValueOperand(); handle_store(ptr, val, frame); return inst->getNextNonDebugInstruction(); @@ -820,9 +846,25 @@ namespace nil { frame.scalars[inst] = stack_memory.load(ptr); return inst->getNextNonDebugInstruction(); } - case llvm::Instruction::PtrToInt: { + case llvm::Instruction::IndirectBr: { ptr_type ptr = resolve_number(frame, inst->getOperand(0)); - assignmnt.public_input(0, public_input_idx) = stack_memory[ptr - 1].offset; + var bb_var = stack_memory.load(ptr); + llvm::BasicBlock *bb = (llvm::BasicBlock *)(resolve_number(bb_var)); + ASSERT(labels.find(bb) != labels.end()); + return &bb->front(); + } + case llvm::Instruction::PtrToInt: { + handle_ptrtoint(inst, inst->getOperand(0), frame); + return inst->getNextNonDebugInstruction(); + } + case llvm::Instruction::IntToPtr: { + std::ostringstream oss; + size_t offset = resolve_number(frame, inst->getOperand(0)); + oss << var_value(assignmnt, frame.scalars[inst->getOperand(0)]).data; + ptr_type ptr = stack_memory.inttoptr(offset); + log.debug("IntToPtr: {} {}", oss.str(), ptr); + ASSERT(ptr != 0); + assignmnt.public_input(0, public_input_idx) = ptr; frame.scalars[inst] = var(0, public_input_idx++, false, var::column_type::public_input); return inst->getNextNonDebugInstruction(); } @@ -960,14 +1002,49 @@ namespace nil { stack_memory.store(ptr, var(0, public_input_idx++, false, var::column_type::public_input)); assignmnt.public_input(0, public_input_idx) = ptr; globals[&global] = var(0, public_input_idx++, false, var::column_type::public_input); + } else if (llvm::isa(initializer)) { + ptr_type ptr = stack_memory.add_cells({layout_resolver->get_type_size(initializer->getType())}); + stack_memory.store(ptr, zero_var); + assignmnt.public_input(0, public_input_idx) = ptr; + globals[&global] = var(0, public_input_idx++, false, var::column_type::public_input); } else { UNREACHABLE("Unhandled global variable"); } } - // Initialize undef var once + // Collect all the possible labels that could be an argument in IndirectBrInst + for (const llvm::Function &function : module) { + for (const llvm::BasicBlock &bb : function) { + for (const llvm::Instruction &inst : bb) { + if (inst.getOpcode() != llvm::Instruction::IndirectBr) { + continue; + } + auto ib = llvm::cast(&inst); + for (const llvm::BasicBlock *succ : ib->successors()) { + if (labels.find(succ) != labels.end()) { + continue; + } + auto label_type = llvm::Type::getInt8PtrTy(module.getContext()); + unsigned label_type_size = layout_resolver->get_type_size(label_type); + ptr_type ptr = stack_memory.add_cells({label_type_size}); + + // Store the pointer to BasicBlock to memory + // TODO(maksenov): avoid C++ pointers in assignment table + assignmnt.public_input(0, public_input_idx) = (const uintptr_t)succ; + stack_memory.store(ptr, var(0, public_input_idx++, false, var::column_type::public_input)); + + assignmnt.public_input(0, public_input_idx) = ptr; + labels[succ] = var(0, public_input_idx++, false, var::column_type::public_input); + } + } + } + } + + // Initialize undef and zero vars once assignmnt.public_input(0, public_input_idx) = typename BlueprintFieldType::value_type(); undef_var = var(0, public_input_idx++, false, var::column_type::public_input); + assignmnt.public_input(0, public_input_idx) = 0; + zero_var = var(0, public_input_idx++, false, var::column_type::public_input); const llvm::Instruction *next_inst = &function.begin()->front(); while (true) { @@ -986,11 +1063,13 @@ namespace nil { const llvm::BasicBlock *predecessor = nullptr; std::stack> call_stack; program_memory stack_memory; - std::map globals; + std::unordered_map globals; + std::unordered_map labels; bool finished = false; size_t public_input_idx = 0; std::unique_ptr layout_resolver; var undef_var; + var zero_var; logger log; };