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 | 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. | 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:: | .. 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 | Note that you will not have to care about these things if you just use | ||||||
| loops and assembly-level functions. | loops and assembly-level functions. | ||||||
| 
 | 
 | ||||||
|  | As an example how this can be done in extreme cases, please see the following. | ||||||
|  | 
 | ||||||
| .. code:: | .. code:: | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         let x := 8 |         let x := 8 | ||||||
|         jump(two) |         jump(two) | ||||||
|         one[1]: |         0 // This code is unreachable but will adjust the stack height correctly | ||||||
|             // Now the stack height is correctly adjusted. |         one: | ||||||
|             x := 9 |             x := 9 // Now x can be accessed properly. | ||||||
|             jump(three) |             jump(three) | ||||||
|  |             pop // Similar negative correction. | ||||||
|         two: |         two: | ||||||
|             7 // push something onto the stack |             7 // push something onto the stack | ||||||
|             jump(one) |             jump(one) | ||||||
|         three: |         three: | ||||||
|  |         pop // We have to pop the manually pushed value here again. | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| .. note:: | .. 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. | outer assembly is visible in a sub-assembly. | ||||||
| 
 | 
 | ||||||
| If control flow passes over the end of a block, pop instructions are inserted | 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 | that match the number of local variables declared in that block. | ||||||
| ``}`` is directly preceded by an opcode that does not have a continuing control | Whenever a local variable is referenced, the code generator needs | ||||||
| flow path. Whenever a local variable is referenced, the code generator needs |  | ||||||
| to know its current relative position in the stack and thus it needs to | to know its current relative position in the stack and thus it needs to | ||||||
| keep track of the current so-called stack height. | keep track of the current so-called stack height. Since all local variables | ||||||
| At the end of a block, this implicit stack height is always reduced by the number | are removed at the end of a block, the stack height before and after the block | ||||||
| of local variables whether there is a continuing control flow or not. | should be the same. If this is not the case, a warning is issued. | ||||||
| 
 |  | ||||||
| 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. |  | ||||||
| 
 | 
 | ||||||
| Why do we use higher-level constructs like ``switch``, ``for`` and functions: | 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. | 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 | The position of all local variables on the stack needs to be known, otherwise | ||||||
| neither references to local variables nor removing local variables automatically | 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, | from the stack at the end of a block will work properly. The desugaring | ||||||
| every label that is preceded by an instruction that ends or diverts control flow | mechanism correctly inserts operations at unreachable blocks that adjust the | ||||||
| should be annotated with the current stack layout. This annotation is performed | stack height properly in case of jumps that do not have a continuing control flow. | ||||||
| automatically during the desugaring phase. |  | ||||||
| 
 | 
 | ||||||
| Example: | Example: | ||||||
| 
 | 
 | ||||||
| @ -730,17 +726,21 @@ After the desugaring phase it looks as follows:: | |||||||
|         $case1: |         $case1: | ||||||
|         { |         { | ||||||
|           // the function call - we put return label and arguments on the stack |           // the function call - we put return label and arguments on the stack | ||||||
|           $ret1 calldataload(4) jump($fun_f) |           $ret1 calldataload(4) jump(f) | ||||||
|           $ret1 [r]: // a label with a [...]-annotation resets the stack height |           // This is unreachable code. Opcodes are added that mirror the | ||||||
|                     // to "current block + number of local variables". It also |           // effect of the function on the stack height: Arguments are | ||||||
|                     // introduces a variable, r: |           // removed and return values are introduced. | ||||||
|                     // r is at top of stack, $0 is below (from enclosing block) |           pop pop | ||||||
|           $ret2 0x20 jump($fun_allocate) |           let r := 0 | ||||||
|           $ret2 [ret]: // stack here: $0, r, ret (top) |           $ret1: // the actual return point | ||||||
|  |           $ret2 0x20 jump($allocate) | ||||||
|  |           pop pop let ret := 0 | ||||||
|  |           $ret2: | ||||||
|           mstore(ret, r) |           mstore(ret, r) | ||||||
|           return(ret, 0x20) |           return(ret, 0x20) | ||||||
|           // although it is useless, the jump is automatically inserted, |           // 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) |           jump($endswitch) | ||||||
|         } |         } | ||||||
|         $caseDefault: |         $caseDefault: | ||||||
| @ -751,20 +751,29 @@ After the desugaring phase it looks as follows:: | |||||||
|         $endswitch: |         $endswitch: | ||||||
|       } |       } | ||||||
|       jump($afterFunction) |       jump($afterFunction) | ||||||
|       $fun_allocate: |       allocate: | ||||||
|       { |       { | ||||||
|         $start[$retpos, size]: |         // we jump over the unreachable code that introduces the function arguments | ||||||
|         // output variables live in the same scope as the 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 |         let pos := 0 | ||||||
|         { |         { | ||||||
|           pos := mload(0x40) |           pos := mload(0x40) | ||||||
|           mstore(0x40, add(pos, size)) |           mstore(0x40, add(pos, size)) | ||||||
|         } |         } | ||||||
|  |         // This code replaces the arguments by the return values and jumps back. | ||||||
|         swap1 pop swap1 jump |         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 y := 0 | ||||||
|         { |         { | ||||||
|           let i := 0 |           let i := 0 | ||||||
| @ -777,8 +786,9 @@ After the desugaring phase it looks as follows:: | |||||||
|           { i := add(i, 1) } |           { i := add(i, 1) } | ||||||
|           jump($for_begin) |           jump($for_begin) | ||||||
|           $for_end: |           $for_end: | ||||||
|         } // Here, a pop instruction is inserted for i |         } // Here, a pop instruction will be inserted for i | ||||||
|         swap1 pop swap1 jump |         swap1 pop swap1 jump | ||||||
|  |         0 0 | ||||||
|       } |       } | ||||||
|       $afterFunction: |       $afterFunction: | ||||||
|       stop |       stop | ||||||
| @ -839,7 +849,7 @@ Grammar:: | |||||||
|     IdentifierOrList = Identifier | '(' IdentifierList ')' |     IdentifierOrList = Identifier | '(' IdentifierList ')' | ||||||
|     IdentifierList = Identifier ( ',' Identifier)* |     IdentifierList = Identifier ( ',' Identifier)* | ||||||
|     AssemblyAssignment = '=:' Identifier |     AssemblyAssignment = '=:' Identifier | ||||||
|     LabelDefinition = Identifier ( '[' ( IdentifierList? | NumberLiteral ) ']' )? ':' |     LabelDefinition = Identifier ':' | ||||||
|     AssemblySwitch = 'switch' FunctionalAssemblyExpression AssemblyCase* |     AssemblySwitch = 'switch' FunctionalAssemblyExpression AssemblyCase* | ||||||
|         ( 'default' ':' AssemblyBlock )? |         ( 'default' ':' AssemblyBlock )? | ||||||
|     AssemblyCase = 'case' FunctionalAssemblyExpression ':' AssemblyBlock |     AssemblyCase = 'case' FunctionalAssemblyExpression ':' AssemblyBlock | ||||||
| @ -872,11 +882,14 @@ Pseudocode:: | |||||||
|     AssemblyFunctionDefinition('function' name '(' arg1, ..., argn ')' '->' ( '(' ret1, ..., retm ')' body) -> |     AssemblyFunctionDefinition('function' name '(' arg1, ..., argn ')' '->' ( '(' ret1, ..., retm ')' body) -> | ||||||
|       <name>: |       <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 |         let ret1 := 0 ... let retm := 0 | ||||||
|         { desugar(body) } |         { 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  |         jump | ||||||
|  |         0 (1 + n times) to compensate removal of arg1, ..., argn and $retPC | ||||||
|       } |       } | ||||||
|     AssemblyFor('for' { init } condition post body) -> |     AssemblyFor('for' { init } condition post body) -> | ||||||
|       { |       { | ||||||
| @ -896,6 +909,7 @@ Pseudocode:: | |||||||
|         pop all local variables that are defined at the current point |         pop all local variables that are defined at the current point | ||||||
|         but not at $forI_end |         but not at $forI_end | ||||||
|         jump($forI_end) |         jump($forI_end) | ||||||
|  |         0 (as many as variables were removed above) | ||||||
|       } |       } | ||||||
|     'continue' -> |     'continue' -> | ||||||
|       { |       { | ||||||
| @ -903,6 +917,7 @@ Pseudocode:: | |||||||
|         pop all local variables that are defined at the current point |         pop all local variables that are defined at the current point | ||||||
|         but not at $forI_continue |         but not at $forI_continue | ||||||
|         jump($forI_continue) |         jump($forI_continue) | ||||||
|  |         0 (as many as variables were removed above) | ||||||
|       } |       } | ||||||
|     AssemblySwitch(switch condition cases ( default: defaultBlock )? ) -> |     AssemblySwitch(switch condition cases ( default: defaultBlock )? ) -> | ||||||
|       { |       { | ||||||
| @ -924,10 +939,13 @@ Pseudocode:: | |||||||
|           { |           { | ||||||
|             // find I such that $funcallI_* does not exist |             // find I such that $funcallI_* does not exist | ||||||
|             $funcallI_return argn  ... arg2 arg1 jump(<name>) |             $funcallI_return argn  ... arg2 arg1 jump(<name>) | ||||||
|  |             pop (n + 1 times) | ||||||
|             if the current context is `let (id1, ..., idm) := f(...)` -> |             if the current context is `let (id1, ..., idm) := f(...)` -> | ||||||
|               $funcallI_return [id1, ..., idm]: |               let id1 := 0 ... let idm := 0 | ||||||
|  |               $funcallI_return: | ||||||
|             else -> |             else -> | ||||||
|               $funcallI_return[m - n - 1]: |               0 (m times) | ||||||
|  |               $funcallI_return: | ||||||
|               turn the functional expression that leads to the function call |               turn the functional expression that leads to the function call | ||||||
|               into a statement stream |               into a statement stream | ||||||
|           } |           } | ||||||
| @ -987,16 +1005,8 @@ Pseudocode:: | |||||||
|       look up id in the syntactic stack of blocks, assert that it is a variable |       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) |       SWAPi where i = 1 + stack_height - stack_height_of_identifier(id) | ||||||
|       POP |       POP | ||||||
|     LabelDefinition(name [id1, ..., idn] :) -> |     LabelDefinition(name:) -> | ||||||
|       JUMPDEST |       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) -> |     NumberLiteral(num) -> | ||||||
|       PUSH<num interpreted as decimal and right-aligned> |       PUSH<num interpreted as decimal and right-aligned> | ||||||
|     HexLiteral(lit) -> |     HexLiteral(lit) -> | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user