mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Remove stack annotations from documentation.
This commit is contained in:
		
							parent
							
								
									369005fc03
								
							
						
					
					
						commit
						8c1d949d1a
					
				| @ -337,8 +337,6 @@ in solidity are: | ||||
| 
 | ||||
| This feature is still a bit cumbersome to use, because the stack offset essentially | ||||
| changes during the call, and thus references to local variables will be wrong. | ||||
| The assumed stack height can be changed by using labels: ``funreturn[-2]:`` | ||||
| The number in brackets should be `m - n - 1`. | ||||
| 
 | ||||
| .. code:: | ||||
| 
 | ||||
| @ -415,19 +413,23 @@ to the stack height just prior to the label. | ||||
| Note that you will not have to care about these things if you just use | ||||
| loops and assembly-level functions. | ||||
| 
 | ||||
| As an example how this can be done in extreme cases, please see the following. | ||||
| 
 | ||||
| .. code:: | ||||
| 
 | ||||
|     { | ||||
|         let x := 8 | ||||
|         jump(two) | ||||
|         one[1]: | ||||
|             // Now the stack height is correctly adjusted. | ||||
|             x := 9 | ||||
|         0 // This code is unreachable but will adjust the stack height correctly | ||||
|         one: | ||||
|             x := 9 // Now x can be accessed properly. | ||||
|             jump(three) | ||||
|             pop // Similar negative correction. | ||||
|         two: | ||||
|             7 // push something onto the stack | ||||
|             jump(one) | ||||
|         three: | ||||
|         pop // We have to pop the manually pushed value here again. | ||||
|     } | ||||
| 
 | ||||
| .. note:: | ||||
| @ -652,17 +654,12 @@ for e.g. returning runtime code or creating contracts. No identifier from an | ||||
| outer assembly is visible in a sub-assembly. | ||||
| 
 | ||||
| If control flow passes over the end of a block, pop instructions are inserted | ||||
| that match the number of local variables declared in that block, unless the | ||||
| ``}`` is directly preceded by an opcode that does not have a continuing control | ||||
| flow path. Whenever a local variable is referenced, the code generator needs | ||||
| that match the number of local variables declared in that block. | ||||
| Whenever a local variable is referenced, the code generator needs | ||||
| to know its current relative position in the stack and thus it needs to | ||||
| keep track of the current so-called stack height. | ||||
| At the end of a block, this implicit stack height is always reduced by the number | ||||
| of local variables whether there is a continuing control flow or not. | ||||
| 
 | ||||
| This means that the stack height before and after the block should be the same. | ||||
| If this is not the case, a warning is issued, | ||||
| unless the last instruction in the block did not have a continuing control flow path. | ||||
| keep track of the current so-called stack height. Since all local variables | ||||
| are removed at the end of a block, the stack height before and after the block | ||||
| should be the same. If this is not the case, a warning is issued. | ||||
| 
 | ||||
| Why do we use higher-level constructs like ``switch``, ``for`` and functions: | ||||
| 
 | ||||
| @ -674,10 +671,9 @@ verification and optimization. | ||||
| Furthermore, if manual jumps are allowed, computing the stack height is rather complicated. | ||||
| The position of all local variables on the stack needs to be known, otherwise | ||||
| neither references to local variables nor removing local variables automatically | ||||
| from the stack at the end of a block will work properly. Because of that, | ||||
| every label that is preceded by an instruction that ends or diverts control flow | ||||
| should be annotated with the current stack layout. This annotation is performed | ||||
| automatically during the desugaring phase. | ||||
| from the stack at the end of a block will work properly. The desugaring | ||||
| mechanism correctly inserts operations at unreachable blocks that adjust the | ||||
| stack height properly in case of jumps that do not have a continuing control flow. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| @ -730,17 +726,21 @@ After the desugaring phase it looks as follows:: | ||||
|         $case1: | ||||
|         { | ||||
|           // the function call - we put return label and arguments on the stack | ||||
|           $ret1 calldataload(4) jump($fun_f) | ||||
|           $ret1 [r]: // a label with a [...]-annotation resets the stack height | ||||
|                     // to "current block + number of local variables". It also | ||||
|                     // introduces a variable, r: | ||||
|                     // r is at top of stack, $0 is below (from enclosing block) | ||||
|           $ret2 0x20 jump($fun_allocate) | ||||
|           $ret2 [ret]: // stack here: $0, r, ret (top) | ||||
|           $ret1 calldataload(4) jump(f) | ||||
|           // This is unreachable code. Opcodes are added that mirror the | ||||
|           // effect of the function on the stack height: Arguments are | ||||
|           // removed and return values are introduced. | ||||
|           pop pop | ||||
|           let r := 0 | ||||
|           $ret1: // the actual return point | ||||
|           $ret2 0x20 jump($allocate) | ||||
|           pop pop let ret := 0 | ||||
|           $ret2: | ||||
|           mstore(ret, r) | ||||
|           return(ret, 0x20) | ||||
|           // although it is useless, the jump is automatically inserted, | ||||
|           // since the desugaring process does not analyze control-flow | ||||
|           // since the desugaring process is a purely syntactic operation that | ||||
|           // does not analyze control-flow | ||||
|           jump($endswitch) | ||||
|         } | ||||
|         $caseDefault: | ||||
| @ -751,20 +751,29 @@ After the desugaring phase it looks as follows:: | ||||
|         $endswitch: | ||||
|       } | ||||
|       jump($afterFunction) | ||||
|       $fun_allocate: | ||||
|       allocate: | ||||
|       { | ||||
|         $start[$retpos, size]: | ||||
|         // output variables live in the same scope as the arguments. | ||||
|         // we jump over the unreachable code that introduces the function arguments | ||||
|         jump($start) | ||||
|         let $retpos := 0 let size := 0 | ||||
|         $start: | ||||
|         // output variables live in the same scope as the arguments and is | ||||
|         // actually allocated. | ||||
|         let pos := 0 | ||||
|         { | ||||
|           pos := mload(0x40) | ||||
|           mstore(0x40, add(pos, size)) | ||||
|         } | ||||
|         // This code replaces the arguments by the return values and jumps back. | ||||
|         swap1 pop swap1 jump | ||||
|         // Again unreachable code that corrects stack height. | ||||
|         0 0 | ||||
|       } | ||||
|       $fun_f: | ||||
|       f: | ||||
|       { | ||||
|         start [$retpos, x]: | ||||
|         jump($start) | ||||
|         let $retpos := 0 let x := 0 | ||||
|         $start: | ||||
|         let y := 0 | ||||
|         { | ||||
|           let i := 0 | ||||
| @ -777,8 +786,9 @@ After the desugaring phase it looks as follows:: | ||||
|           { i := add(i, 1) } | ||||
|           jump($for_begin) | ||||
|           $for_end: | ||||
|         } // Here, a pop instruction is inserted for i | ||||
|         } // Here, a pop instruction will be inserted for i | ||||
|         swap1 pop swap1 jump | ||||
|         0 0 | ||||
|       } | ||||
|       $afterFunction: | ||||
|       stop | ||||
| @ -839,7 +849,7 @@ Grammar:: | ||||
|     IdentifierOrList = Identifier | '(' IdentifierList ')' | ||||
|     IdentifierList = Identifier ( ',' Identifier)* | ||||
|     AssemblyAssignment = '=:' Identifier | ||||
|     LabelDefinition = Identifier ( '[' ( IdentifierList? | NumberLiteral ) ']' )? ':' | ||||
|     LabelDefinition = Identifier ':' | ||||
|     AssemblySwitch = 'switch' FunctionalAssemblyExpression AssemblyCase* | ||||
|         ( 'default' ':' AssemblyBlock )? | ||||
|     AssemblyCase = 'case' FunctionalAssemblyExpression ':' AssemblyBlock | ||||
| @ -872,11 +882,14 @@ Pseudocode:: | ||||
|     AssemblyFunctionDefinition('function' name '(' arg1, ..., argn ')' '->' ( '(' ret1, ..., retm ')' body) -> | ||||
|       <name>: | ||||
|       { | ||||
|         $<name>_start [$retPC, $argn, ..., arg1]: | ||||
|         jump($<name>_start) | ||||
|         let $retPC := 0 let argn := 0 ... let arg1 := 0 | ||||
|         $<name>_start: | ||||
|         let ret1 := 0 ... let retm := 0 | ||||
|         { desugar(body) } | ||||
|         swap and pop items so that only ret1, ... retn, $retPC are left on the stack | ||||
|         swap and pop items so that only ret1, ... retm, $retPC are left on the stack | ||||
|         jump | ||||
|         0 (1 + n times) to compensate removal of arg1, ..., argn and $retPC | ||||
|       } | ||||
|     AssemblyFor('for' { init } condition post body) -> | ||||
|       { | ||||
| @ -896,6 +909,7 @@ Pseudocode:: | ||||
|         pop all local variables that are defined at the current point | ||||
|         but not at $forI_end | ||||
|         jump($forI_end) | ||||
|         0 (as many as variables were removed above) | ||||
|       } | ||||
|     'continue' -> | ||||
|       { | ||||
| @ -903,6 +917,7 @@ Pseudocode:: | ||||
|         pop all local variables that are defined at the current point | ||||
|         but not at $forI_continue | ||||
|         jump($forI_continue) | ||||
|         0 (as many as variables were removed above) | ||||
|       } | ||||
|     AssemblySwitch(switch condition cases ( default: defaultBlock )? ) -> | ||||
|       { | ||||
| @ -924,10 +939,13 @@ Pseudocode:: | ||||
|           { | ||||
|             // find I such that $funcallI_* does not exist | ||||
|             $funcallI_return argn  ... arg2 arg1 jump(<name>) | ||||
|             pop (n + 1 times) | ||||
|             if the current context is `let (id1, ..., idm) := f(...)` -> | ||||
|               $funcallI_return [id1, ..., idm]: | ||||
|               let id1 := 0 ... let idm := 0 | ||||
|               $funcallI_return: | ||||
|             else -> | ||||
|               $funcallI_return[m - n - 1]: | ||||
|               0 (m times) | ||||
|               $funcallI_return: | ||||
|               turn the functional expression that leads to the function call | ||||
|               into a statement stream | ||||
|           } | ||||
| @ -987,16 +1005,8 @@ Pseudocode:: | ||||
|       look up id in the syntactic stack of blocks, assert that it is a variable | ||||
|       SWAPi where i = 1 + stack_height - stack_height_of_identifier(id) | ||||
|       POP | ||||
|     LabelDefinition(name [id1, ..., idn] :) -> | ||||
|     LabelDefinition(name:) -> | ||||
|       JUMPDEST | ||||
|       // register new local variables id1, ..., idn and adjust the stack | ||||
|       // height such that it matches the stack height at the beginning of | ||||
|       // the block plus all local variables including the just registered | ||||
|       // ones where idn is at the stack top. If n is zero, resets the stack | ||||
|       // height to just the local variables. | ||||
|     LabelDefinition(name [number] :) -> | ||||
|       JUMPDEST | ||||
|       // adjust stack height by the relative amount "number" (can be negative) | ||||
|     NumberLiteral(num) -> | ||||
|       PUSH<num interpreted as decimal and right-aligned> | ||||
|     HexLiteral(lit) -> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user