Skip to content

Commit

Permalink
Merge pull request #126 from nevillegrech/function_inference_improvement
Browse files Browse the repository at this point in the history
Support for some conditional call patterns + cloning improvement
  • Loading branch information
sifislag authored Feb 7, 2024
2 parents 20df5a7 + f7efba9 commit c17d4f7
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 6 deletions.
36 changes: 35 additions & 1 deletion logic/functions.dl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ OptNotImmediateBlockJumpTarget(retCtx, retBlock, targetVariable, retTarget) :-
!postTrans.ImmediateBlockJumpTarget(retBlock, targetVariable).



/**
Call-return pattern:
a) basic block jumps to a valid "non-locally-derived" address
Expand All @@ -75,6 +74,41 @@ PossibleReturnAddressWithPos(targetSetter, retCtx, retBlock, retTarget, pos) :-
postTrans.JUMP(jumpStmt),
postTrans.BeforeLocalStackContents(jumpStmt, pos, targetVariable).

/**
This pattern is prominent in 0.8.x code:
Block label_a:
push continuation
...
push label_b
jumpi
fallthrough:
...
revert
Block label_b:
push func_start
jump

We handle it by treating the return address as if it was pushed by label_b.

Code older than 0.8.x would call the function directly with a jumpi.
We try to filter this out and will need to handle it separately.
*/
PossibleReturnAddressWithPos(as(next, Block), retCtx, retBlock, retTarget, pos) :-
OptNotImmediateBlockJumpTarget(retCtx, retBlock, targetVariable, retTarget),
postTrans.Statement_Defines(targetSetStatement, targetVariable),
postTrans.Statement_Block(targetSetStatement, targetSetter),
postTrans.BasicBlock_Tail(targetSetter, jumpiStmt),
postTrans.JUMPI(jumpiStmt),
postTrans.LocalStackContents(jumpiStmt, stackIndex, targetVariable),
postTrans.FallthroughEdge(targetSetter, revertBlock),
postTrans.BlockWillRevert(revertBlock),
postTrans.StaticBlockJumpTarget(targetSetter, next),
postTrans.BasicBlock_Tail(as(next, Block), jumpStmt),
postTrans.JUMP(jumpStmt),
postTrans.BeforeLocalStackContents(jumpStmt, pos, stackIndex),
// filter out cases where the function is called directly
!postTrans.IsJumpBlockPrivateFunctionCall(_, as(next, Block)).

.decl OptPossibleReturnAddressWithPos(targetSetter:Block, retBlock:Block, retTarget:Block, pos:number)
OptPossibleReturnAddressWithPos(targetSetter, retBlock, retTarget, pos) :-
PossibleReturnAddressWithPos(targetSetter, _, retBlock, retTarget, pos).
Expand Down
74 changes: 70 additions & 4 deletions logic/local.dl
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ ByteCodeHex(substr(bytecode, 2, strlen(bytecode))):-
BeforeLocalStackContents(stmt, n-1, duplicated),
Statement_Opcode(stmt, opcode),
DUPN(opcode, n).

LocalStackContents(stmt, m+1, other) :-
BeforeLocalStackContents(stmt, m, other),
Statement_Opcode(stmt, opcode),
Expand Down Expand Up @@ -453,8 +453,17 @@ ByteCodeHex(substr(bytecode, 2, strlen(bytecode))):-
IsStackIndexLessThan(n, m),
BeforeLocalStackContents(stmt, n, varOrStackIndex).

// Identify low-level blocks that will always REVERT
.decl BlockWillRevert(revertBlock: Block)
BlockWillRevert(revertBlock):-
Statement_Block(revert, revertBlock),
REVERT(revert).


BlockWillRevert(indirectRevertBlock):-
BlockWillRevert(revertBlock),
StaticBlockJumpTarget(indirectRevertBlock, as(revertBlock, Value)),
Statement_Block(jump, indirectRevertBlock),
JUMP(jump).

.decl IsJump(stmt:Statement)

Expand Down Expand Up @@ -601,7 +610,14 @@ ByteCodeHex(substr(bytecode, 2, strlen(bytecode))):-
callee = as(calleeValue, Block),
(CallBlockEarliestContinuation(caller, callee, firstCont);
(!CallBlockEarliestContinuation(caller, callee, _), firstCont = "undefined")).


// Helper to identify blocks that are very likely function entries
.decl IsJumpBlockPrivateFunctionCall(caller: Block, callee: Block)
IsJumpBlockPrivateFunctionCall(caller, callee):-
PrivateFunctionCall(caller, callee, _, _),
Statement_Block(call, caller),
JUMP(call).

.decl MultiplePrivateFunctionCall(caller: Block)
MultiplePrivateFunctionCall(caller) :-
PrivateFunctionCall(caller, callee, cont1, _),
Expand Down Expand Up @@ -702,6 +718,31 @@ ByteCodeHex(substr(bytecode, 2, strlen(bytecode))):-
ByteCodeHex(bytecodeStr),
const = cat("0x", substr(bytecodeStr, 2*codeOffsetNum, 2*smallNum)).

.decl PushedLabelOccupiesMultipleIndexes(block: Block, var: Variable)
PushedLabelOccupiesMultipleIndexes(block, var):-
JUMPDEST(as(val, symbol)),
Variable_Value(var, val),
Statement_Defines(stmt, var),
Statement_Block(stmt, block),
BasicBlock_Tail(block, call),
LocalStackContents(call, index1, var),
LocalStackContents(call, index2, var),
index1 != index2,
!BlockUsesLocal(block, var).

/**
Identify cases where the same continuation is pushed once but DUPed
into multiple stack indexes
*/
.decl DUPStatementCopiesPushedLabelVar(dup: Statement, label: Value)
DUPStatementCopiesPushedLabelVar(dup, label):-
PushedLabelOccupiesMultipleIndexes(block, var),
Statement_Block(dup, block),
Statement_Opcode(dup, opcode),
DUPN(opcode, n),
BeforeLocalStackContents(dup, n-1, var),
Variable_Value(var, label).

IsOptionalSelector(selectorVarOrIndex):-
BlockComparesSig(_, _, selectorVarOrIndex);
BlockComparesSigFallthroughSolidity(_, _, selectorVarOrIndex);
Expand Down Expand Up @@ -942,7 +983,32 @@ COPY_CODE(revertCloner, factReader)
#ifdef BLOCK_CLONING
// Enable the early cloning of blocks to increase precision (sacrificing performance)
.init blockCloner = BLOCK_CLONING
COPY_OUTPUT(blockCloner, revertCloner)
// Old logic: Copy the output of revertCloner to the blockCloner
//COPY_OUTPUT(blockCloner, revertCloner)

/**
Transformation that helps the HeuristicBlockCloner
*/
.init clonerHelperInsertor = StatementInsertor
COPY_OUTPUT(clonerHelperInsertor, revertCloner)
.init beforeClonerLocal = PreTransLocalAnalysis
COPY_OUTPUT(beforeClonerLocal, revertCloner)

/**
If a `DUPx` statement copies an escaping jumpdest, replace the `DUP` with a `PUSH` of the same constant.
This helps the `HeuristicBlockCloner` (which clones blocks on a per-push basis) differentiate the two uses
of the same continuation, and clone if needed.
*/
clonerHelperInsertor.removeOp(dup),
clonerHelperInsertor.insertOps(dup,
LIST(
STMT(PUSH4, label)
)
) :-
beforeClonerLocal.DUPStatementCopiesPushedLabelVar(dup, label).

COPY_OUTPUT(blockCloner, clonerHelperInsertor)
// Transformation doesn't introduce new blocks, we can use revertCloner for the original block info
blockCloner.Prev_Block_OriginalBlock(block, originalBlock):- revertCloner.Block_OriginalBlock(block, originalBlock).

.init preTrans = PreTransLocalAnalysis
Expand Down
2 changes: 1 addition & 1 deletion logic/statement_insertor.dl
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ to.PublicFunction(block, funHex, selector):- from.PublicFunction(block, funHex,

MaxOriginalStatement(as(maxStmt, Statement)):-
maxStmtNumber = max stmtNumber : { Statement_Opcode(stmt, _), stmtNumber = @hex_to_number(stmt)},
maxStmt = @number_to_hex(maxStmtNumber).
maxStmt = @number_to_hex(2*maxStmtNumber).

BlockSize(block, @hex_to_number(tail) - @hex_to_number(block)):-
analysis.BasicBlock_Tail(block, tail).
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"client_path": null,
"gigahorse_args": ["-i", "--disable_inline", "--disable_scalable_fallback", "--early_cloning"],
"expected_analytics": [["Analytics_JumpToMany", 0, 0], ["Analytics_StmtMissingOperand", 0, 0],
["Analytics_UnreachableBlock", 0 ,0], ["Analytics_BlockHasNoTACBlock", 0, 0],
["Analytics_PrivateFunctionMatchesMetadata", 58, 0],
["Analytics_PrivateFunctionMatchesMetadataIncorrectArgs", 0, 0], ["Analytics_PrivateFunctionMatchesMetadataIncorrectReturnArgs", 0, 0]]
}
Loading

0 comments on commit c17d4f7

Please sign in to comment.