Skip to content

Commit

Permalink
Return and modifiers (#113)
Browse files Browse the repository at this point in the history
* different implementation of return

* update tests
  • Loading branch information
dfilaretti committed Apr 26, 2018
1 parent 29a62cf commit c927b40
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 38 deletions.
10 changes: 9 additions & 1 deletion iele-example-tests/auction/auction.iele.ref
Expand Up @@ -68,6 +68,8 @@ entry:
%timestamp = call @iele.timestamp()
%tmp = add %timestamp, %_biddingTime_1
sstore %tmp, @"auctionEnd"

return:
ret void

throw:
Expand Down Expand Up @@ -101,6 +103,8 @@ if.end:
sstore %caller, @"highestBidder"
%callvalue10 = call @iele.callvalue()
sstore %callvalue10, @"highestBid"

return:
ret void

throw:
Expand Down Expand Up @@ -130,7 +134,11 @@ entry:
%status = call @"deposit" at %beneficiary.val () send %highestBid.val, gaslimit %gas
br %status, throw.status
%highestBidder.val = sload @"highestBidder"
ret %highestBidder.val
%_2 = %highestBidder.val
br return

return:
ret %_2

throw:
revert -1
Expand Down
2 changes: 1 addition & 1 deletion iele-example-tests/auction/create.iele.json
Expand Up @@ -14,7 +14,7 @@
{
"out": ["0x6295ee1b4f6dd65047762f924ecd367c17eabf8f"],
"status": "",
"gas": "0x0cb0c3",
"gas": "0x0ca20f",
"logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"refund": ""
}
Expand Down
2 changes: 1 addition & 1 deletion iele-example-tests/auction/settle_success.iele.json
Expand Up @@ -26,7 +26,7 @@
{
"out": ["0x2000000000000000000000000000000000000000"],
"status": "",
"gas": "0x0f70d3",
"gas": "0x0f70c2",
"logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"refund": ""
}
Expand Down
112 changes: 82 additions & 30 deletions libsolidity/codegen/IeleCompiler.cpp
Expand Up @@ -465,6 +465,8 @@ bool IeleCompiler::visit(const FunctionDefinition &function) {
// We store the formal argument names, which we'll use when generating in-range
// checks in case of a public function.
std::vector<iele::IeleArgument *> parameters;
// returnParameters.clear(); // otherwise, stuff keep getting added, regardless
// // of which function we are in (i.e. it breaks)

// Visit formal arguments.
for (const ASTPointer<const VariableDeclaration> &arg : function.parameters()) {
Expand All @@ -486,7 +488,7 @@ bool IeleCompiler::visit(const FunctionDefinition &function) {
std::string genName = ret->name() + getNextVarSuffix();
ReturnParameterNames.push_back(genName);
ReturnParameterTypes.push_back(ret->type());
iele::IeleLocalVariable::Create(&Context, genName, CompilingFunction);
CompilingFunctionReturnParameters.push_back(iele::IeleLocalVariable::Create(&Context, genName, CompilingFunction));
// No need to keep track of the mapping for omitted return params, since
// they will never be referenced.
if (!(ret->name() == ""))
Expand Down Expand Up @@ -558,27 +560,65 @@ bool IeleCompiler::visit(const FunctionDefinition &function) {
llvm::cast<iele::IeleLocalVariable>(RetParam), &*ret);
}

// Make return block
iele::IeleBlock *retBlock = iele::IeleBlock::Create(
&Context, "return");

// Add it to stack of return locations
ReturnBlocks.push(retBlock);

// Visit function body (inc modifiers).
CompilingFunctionASTNode = &function;
ModifierDepth = -1;
appendModifierOrFunctionCode();

// Add a ret if the last block doesn't end with a ret instruction.
if (!CompilingBlock->endsWithRet()) {
if (function.returnParameters().size() == 0) { // add a ret void
iele::IeleInstruction::CreateRetVoid(CompilingBlock);
} else { // return declared parameters
llvm::SmallVector<iele::IeleValue *, 4> Returns;

// Find Symbol Table for this function
iele::IeleValueSymbolTable *ST =
CompilingFunction->getIeleValueSymbolTable();
solAssert(ST,
"IeleCompiler: failed to access compiling function's symbol "
"table.");

// Prepare arguments for the `ret` instruction by fetching the param names
for (unsigned i = 0; i < ReturnParameterNames.size(); i++) {
// Append return block
appendReturn(function, ReturnParameterNames, ReturnParameterTypes);

// Append the exception blocks if needed.
appendRevertBlocks();

CompilingBlock = nullptr;
CompilingFunction = nullptr;
CompilingFunctionReturnParameters.clear(); // otherwise, stuff keep getting added, regardless
// of which function we are in (i.e. it breaks)
return false;
}

void IeleCompiler::appendReturn(const FunctionDefinition &function,
llvm::SmallVector<std::string, 4> ReturnParameterNames,
llvm::SmallVector<TypePointer, 4> ReturnParameterTypes) {

solAssert(!ReturnBlocks.empty(), "IeleCompiler: appendReturn error");

auto retBlock = ReturnBlocks.top();

// Append block
retBlock -> insertInto(CompilingFunction);

// Set it as currently compiling block
CompilingBlock = retBlock;

if (function.returnParameters().size() == 0) { // add a ret void
iele::IeleInstruction::CreateRetVoid(CompilingBlock);
} else { // return declared parameters
llvm::SmallVector<iele::IeleValue *, 4> Returns;

// Find Symbol Table for this function
iele::IeleValueSymbolTable *ST =
CompilingFunction->getIeleValueSymbolTable();
solAssert(ST,
"IeleCompiler: failed to access compiling function's symbol "
"table.");

// Prepare arguments for the `ret` instruction by fetching the param names
// for (const std::string paramName : ReturnParameterNames) {
// iele::IeleValue *param = ST->lookup(paramName);
// solAssert(param, "IeleCompiler: couldn't find return parameter name in symbol table:");
// Returns.push_back(param);
// }

for (unsigned i = 0; i < ReturnParameterNames.size(); i++) {
const std::string paramName = ReturnParameterNames[i];
TypePointer paramType = ReturnParameterTypes[i];
iele::IeleValue *param = ST->lookup(paramName);
Expand All @@ -590,17 +630,8 @@ bool IeleCompiler::visit(const FunctionDefinition &function) {
}
}

// Create `ret` instruction
iele::IeleInstruction::CreateRet(Returns, CompilingBlock);
}
iele::IeleInstruction::CreateRet(Returns, CompilingBlock);
}

// Append the exception blocks if needed.
appendRevertBlocks();

CompilingBlock = nullptr;
CompilingFunction = nullptr;
return false;
}

void IeleCompiler::appendRevertBlocks(void) {
Expand Down Expand Up @@ -678,9 +709,10 @@ bool IeleCompiler::visit(const IfStatement &ifStatement) {
bool IeleCompiler::visit(const Return &returnStatement) {
const Expression *returnExpr = returnStatement.expression();

solAssert(!ReturnBlocks.empty(), "IeleCompiler: return jmp destination not set");

if (!returnExpr) {
// Create ret void.
iele::IeleInstruction::CreateRetVoid(CompilingBlock);
connectWithUnconditionalJump(CompilingBlock, ReturnBlocks.top());
return false;
}

Expand All @@ -704,7 +736,13 @@ bool IeleCompiler::visit(const Return &returnStatement) {
EncodedReturnValues.push_back(Value);
}
}
iele::IeleInstruction::CreateRet(EncodedReturnValues, CompilingBlock);

for (unsigned i = 0; i < EncodedReturnValues.size(); ++i) {
iele::IeleInstruction::CreateAssign(
CompilingFunctionReturnParameters[i], EncodedReturnValues[i], CompilingBlock);
}

connectWithUnconditionalJump(CompilingBlock, ReturnBlocks.top());

return false;
}
Expand Down Expand Up @@ -797,7 +835,21 @@ void IeleCompiler::appendModifierOrFunctionCode() {

// Visit whatever is next (modifier's body or function body)
if (codeBlock) {
iele::IeleBlock *JumpTarget;
if (ModifierDepth != 0) {
JumpTarget = iele::IeleBlock::Create(
&Context, "ret_jmp_dest");
ReturnBlocks.push(JumpTarget);
}

codeBlock->accept(*this);

if (ModifierDepth != 0) {
JumpTarget -> insertInto(CompilingFunction);
CompilingBlock = JumpTarget;
ReturnBlocks.pop();
}

}

ModifierDepth--;
Expand Down
10 changes: 9 additions & 1 deletion libsolidity/codegen/IeleCompiler.h
Expand Up @@ -141,11 +141,19 @@ class IeleCompiler : public ASTConstVisitor {
// Infrastructure for handling modifiers (borrowed from ContractCompiler.cpp)
// Lookup function modifier by name
const ModifierDefinition &functionModifier(const std::string &_name) const;
void appendReturn(const FunctionDefinition &function,
llvm::SmallVector<std::string, 4> ReturnParameterNames,
llvm::SmallVector<TypePointer, 4> ReturnParameterTypes);
// Appends one layer of function modifier code of the current function, or the
// function body itself if the last modifier was reached.
void appendModifierOrFunctionCode();
unsigned ModifierDepth;

// Maps each level of modifiers with a return target
std::stack<iele::IeleBlock *> ReturnBlocks;
// Return parameters of the current function
// TODO: do we really want to have this here?
std::vector<iele::IeleLocalVariable *> CompilingFunctionReturnParameters;

template <class ArgClass, class ReturnClass, class ExpressionClass>
void compileFunctionArguments(ArgClass *Arguments, ReturnClass *Returns, const::std::vector<ASTPointer<ExpressionClass>> &arguments, const FunctionType &function, bool encode);

Expand Down
4 changes: 0 additions & 4 deletions test/libsolidity/SolidityEndToEndTest.cpp
Expand Up @@ -2759,7 +2759,6 @@ BOOST_AUTO_TEST_CASE(function_modifier)
ABI_CHECK(callContractFunctionWithValue("getOne()", 1), encodeArgs(1));
}

BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(function_modifier_local_variables, 1)
BOOST_AUTO_TEST_CASE(function_modifier_local_variables)
{
char const* sourceCode = R"(
Expand Down Expand Up @@ -2799,7 +2798,6 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation)
ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(2));
}

BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(function_modifier_multi_with_return, 1)
BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return)
{
// Note that return sets the return variable and jumps to the end of the current function or
Expand Down Expand Up @@ -2884,7 +2882,6 @@ BOOST_AUTO_TEST_CASE(function_modifier_multiple_times)
ABI_CHECK(callContractFunction("a()"), encodeArgs(2 + 5 + 3));
}

BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(function_modifier_multiple_times_local_vars, 1)
BOOST_AUTO_TEST_CASE(function_modifier_multiple_times_local_vars)
{
char const* sourceCode = R"(
Expand Down Expand Up @@ -8756,7 +8753,6 @@ BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length)
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7)));
}

BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(return_does_not_skip_modifier, 1)
BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier)
{
char const* sourceCode = R"(
Expand Down

0 comments on commit c927b40

Please sign in to comment.