tracing aiken build: proofread
This commit is contained in:
		
							parent
							
								
									e2db317b30
								
							
						
					
					
						commit
						b340cfd2f0
					
				|  | @ -8,7 +8,7 @@ Aims: | ||||||
| 
 | 
 | ||||||
| The motivation for writing this came from a desire to add additional features to Aiken not yet available. | The motivation for writing this came from a desire to add additional features to Aiken not yet available. | ||||||
| One such feature would evaluate an arbitrary function in Aiken callable from JavaScript.  | One such feature would evaluate an arbitrary function in Aiken callable from JavaScript.  | ||||||
| This would help a lot with testing trying to align on and off-chain code.  | This would help a lot with testing and when trying to align on and off-chain code.  | ||||||
| 
 | 
 | ||||||
| Another more pipe dreamy, ad-hoc function extraction - from a span of code, generate a function. | Another more pipe dreamy, ad-hoc function extraction - from a span of code, generate a function. | ||||||
| A digression to answer _why would this be at all helpful?!_ | A digression to answer _why would this be at all helpful?!_ | ||||||
|  | @ -23,9 +23,9 @@ Possible solutions: | ||||||
| The problems are: | The problems are: | ||||||
| 
 | 
 | ||||||
| 1. Requires relentless constructing and deconstructing across the function call. | 1. Requires relentless constructing and deconstructing across the function call. | ||||||
| And this is adds costs in Aiken.  | This adds costs.  | ||||||
| 2. Becomes tedious aligning the definition and function call.   | 2. Becomes tedious aligning the definition and function call.   | ||||||
| 3. End up with very long validators which are hard to unit test.  | 3. Ends up with very long validators which are hard to unit test.  | ||||||
| 
 | 
 | ||||||
| My current preferred way is to accept that validator functions are long. | My current preferred way is to accept that validator functions are long. | ||||||
| Ad-hoc function extraction would allow for sections of code to be tested without needing to be factored out. | Ad-hoc function extraction would allow for sections of code to be tested without needing to be factored out. | ||||||
|  | @ -35,18 +35,17 @@ To do either of these, we need to get to grips with the Aiken compilation pipeli | ||||||
| ### This won't age well  | ### This won't age well  | ||||||
| 
 | 
 | ||||||
| Aiken is undergoing active development.  | Aiken is undergoing active development.  | ||||||
| This post was started life with Aiken ~v1.14.  | This post started life with Aiken ~v1.14.  | ||||||
| With Aiken v1.15, there were already reasonably significant changes to the compilation pipeline.  | Aiken v1.15 introduced reasonably significant changes to the compilation pipeline.  | ||||||
| The word is that there aren't as big changes in the near future,  | The word is that there aren't any more big changes in the near future,  | ||||||
| but this article will undoubtedly begin to diverge from the current code-base even before publishing.   | but this article will undoubtedly begin to diverge from the current code-base even before publishing.   | ||||||
| 
 | 
 | ||||||
| ### Limitations of narrating code | ### Limitations of narrating code | ||||||
| 
 | 
 | ||||||
| Narrating code becomes a compromise between being honest and accurate, and being readable and digestible.  | Narrating code becomes a compromise between being honest and accurate, and being readable and digestible.  | ||||||
| Following the command `aiken build` covers well in excess of 10,000 LoC. | The command `aiken build` covers well in excess of 10,000 LoC. | ||||||
| The writing of this post ground slowly to a halt as it progressed deeper into the code  | The writing of this post ground to a halt as it reached deeper into the code-base. | ||||||
| with the details seeming to increase in importance.  | To redeem it, some (possibly large) sections remain black boxes. | ||||||
| At some point I had to draw a line and resign to fact that some parts will remain black boxes for now.  |  | ||||||
| 
 | 
 | ||||||
| ## Aiken build | ## Aiken build | ||||||
| 
 | 
 | ||||||
|  | @ -67,7 +66,7 @@ At a high level we are trying to do something straightforward: reformulate Aiken | ||||||
| Some Aiken expressions are relatively easy to handle for example an Aiken `Int` goes to an `Int` in Uplc.  | Some Aiken expressions are relatively easy to handle for example an Aiken `Int` goes to an `Int` in Uplc.  | ||||||
| Some Aiken expressions require more involved handling, for example an Aiken `If... If Else... Else `  | Some Aiken expressions require more involved handling, for example an Aiken `If... If Else... Else `  | ||||||
| must have the branches "nested" in Uplc. | must have the branches "nested" in Uplc. | ||||||
| Aiken also have lots of nice-to-haves like pattern matching, modules, and generics. | Aiken has lots of nice-to-haves like pattern matching, modules, and generics; | ||||||
| Uplc has none of these. | Uplc has none of these. | ||||||
| 
 | 
 | ||||||
| ### The Preamble  | ### The Preamble  | ||||||
|  | @ -114,9 +113,9 @@ Things become a bit intimidating at this point in terms of sheer lines of code: | ||||||
| `gen_uplc.rs` and three modules in `gen_uplc/` totals > 8500 LoC.   | `gen_uplc.rs` and three modules in `gen_uplc/` totals > 8500 LoC.   | ||||||
| 
 | 
 | ||||||
| Aiken has its own _intermediate representation_ called `air` (as in Aiken Intermediate Representation).  | Aiken has its own _intermediate representation_ called `air` (as in Aiken Intermediate Representation).  | ||||||
| These are common in compiled languages. | Intermediate representations are common in compiled languages. | ||||||
| `Air` is defined in `aiken-lang/src/gen_uplc/air.rs`.  | `Air` is defined in `aiken-lang/src/gen_uplc/air.rs`.  | ||||||
| Unsurprisingly, it looks little bit like a language between Aiken and plutus.  | Unsurprisingly, it looks a little bit like a language between Aiken and plutus.  | ||||||
| 
 | 
 | ||||||
| In fact, Aiken has another intermediate representation: `AirTree`.  | In fact, Aiken has another intermediate representation: `AirTree`.  | ||||||
| This is constructed between the `TypedExpr` and `Vec<Air>` ie between parsed Aiken and air.  | This is constructed between the `TypedExpr` and `Vec<Air>` ie between parsed Aiken and air.  | ||||||
|  | @ -134,12 +133,12 @@ It does so by | ||||||
|   self.assignment >> self.expect_type_assign >> self.code_gen_functions.insert |   self.assignment >> self.expect_type_assign >> self.code_gen_functions.insert | ||||||
| ``` | ``` | ||||||
| and thus is creating a hashmap of all the functions that appear in the definition. | and thus is creating a hashmap of all the functions that appear in the definition. | ||||||
| From the call to return of `assign` covers > 600 LoC so we'll leave this as otherwise a black box. | From the call to return of `assign` covers > 600 LoC so we'll leave this as a black box. | ||||||
| (`self.handle_each_clause` is also called with `mut` which in turn calls `self.build` for which `mut` it is needed.)  | (`self.handle_each_clause` is also called with `mut` which in turn calls `self.build` for which `mut` it is needed.)  | ||||||
| 
 | 
 | ||||||
| Validators in Aiken are boolean functions while in Uplc they are unit-valued (aka void-valued) functions. | Validators in Aiken are boolean functions while in Uplc they are unit-valued (aka void-valued) functions. | ||||||
| Thus the air tree is wrapped such that `false` results in an error (`wrap_validator_condition`).  | Thus the air tree is wrapped such that `false` results in an error (`wrap_validator_condition`).  | ||||||
| I don't know why there is a prevailing thought that boolean functions are preferable than functions  | I don't know why there is a prevailing thought that boolean functions are preferable to functions  | ||||||
| that error if anything is wrong - which is what validators are. | that error if anything is wrong - which is what validators are. | ||||||
| 
 | 
 | ||||||
| `check_validator_args` again extends the airtree from the previous step,  | `check_validator_args` again extends the airtree from the previous step,  | ||||||
|  | @ -186,7 +185,7 @@ As `AirTree` is for internal use only, the scope for potential problems is reaso | ||||||
| It seems likely this is to avoid similar-yet-different IRs between steps. | It seems likely this is to avoid similar-yet-different IRs between steps. | ||||||
| However, the trade off is that it partially obfuscates what is a valid state where.  | However, the trade off is that it partially obfuscates what is a valid state where.  | ||||||
| 
 | 
 | ||||||
| What is hoisting? hoisting gives the airtree depth.  | What is hoisting? Hoisting gives the airtree depth.  | ||||||
| The motivation is that by the time we hit Uplc it is "generally better" | The motivation is that by the time we hit Uplc it is "generally better" | ||||||
| that  | that  | ||||||
| 
 | 
 | ||||||
|  | @ -194,7 +193,7 @@ that | ||||||
| - the definition appears as close to use as possible  | - the definition appears as close to use as possible  | ||||||
| 
 | 
 | ||||||
| Hoisting creates tree paths.  | Hoisting creates tree paths.  | ||||||
| The final airtree to airtree step is`self.hoist_functions_to_validator` traverses the paths. | The final airtree to airtree step, `self.hoist_functions_to_validator`, traverses these paths. | ||||||
| There is a lot of mutating of self, making it quite hard to keep a handle on things.  | There is a lot of mutating of self, making it quite hard to keep a handle on things.  | ||||||
| In all this (several thousand?) LoC, it is essentially ascertaining in which node of the tree | In all this (several thousand?) LoC, it is essentially ascertaining in which node of the tree | ||||||
| to insert each function definition.  | to insert each function definition.  | ||||||
|  | @ -220,7 +219,7 @@ It flattens the tree to a vec. | ||||||
| Next we go from `Vec<Air> -> Term<Name>`. | Next we go from `Vec<Air> -> Term<Name>`. | ||||||
| This step is a little more involved than the previous.  | This step is a little more involved than the previous.  | ||||||
| For one, this is executed in the context of the code generator.  | For one, this is executed in the context of the code generator.  | ||||||
| Moreover, the code generator is treated mutable - ouch. | Moreover, the code generator is treated as mutable - ouch. | ||||||
| 
 | 
 | ||||||
| On further inspection we see that the only mutation is setting `self.needs_field_access = true`. | On further inspection we see that the only mutation is setting `self.needs_field_access = true`. | ||||||
| This flag informs the compiler that, if true, additional terms must be added in one of the final steps | This flag informs the compiler that, if true, additional terms must be added in one of the final steps | ||||||
|  | @ -232,7 +231,7 @@ Some examples: | ||||||
| 
 | 
 | ||||||
| - `Air::Var` require 100 LoC to do case handling on different constructors.  | - `Air::Var` require 100 LoC to do case handling on different constructors.  | ||||||
| - Lists in air have no immediate analogue in uplc | - Lists in air have no immediate analogue in uplc | ||||||
| - builtins, as in built-in functions (standard shorthand), have to mediated  | - builtins, as in built-in functions (standard shorthand), have to be mediated  | ||||||
| with some combination of `force` and `delay` in order to behave as they should. | with some combination of `force` and `delay` in order to behave as they should. | ||||||
| - user functions must be "uncurried", ie treated as a sequence of single argument functions,  | - user functions must be "uncurried", ie treated as a sequence of single argument functions,  | ||||||
| and recursion must be handled | and recursion must be handled | ||||||
|  | @ -240,9 +239,8 @@ and recursion must be handled | ||||||
| 
 | 
 | ||||||
| #### Cranking the Optimizer | #### Cranking the Optimizer | ||||||
| 
 | 
 | ||||||
| There is a sequence of operations performed on the Uplc mapping `Term<Name> -> Term<Name>`. | There is a sequence of operations performed on the Uplc, mapping `Term<Name> -> Term<Name>`. | ||||||
| These remove inconsequential parts of the logic which will appear. | This removes inconsequential parts of the logic which have been generated, including:  | ||||||
| These include:  |  | ||||||
| 
 | 
 | ||||||
| - removing application of the identity function | - removing application of the identity function | ||||||
| - directly substituting where apply lambda is applied to a constant or builtin | - directly substituting where apply lambda is applied to a constant or builtin | ||||||
|  | @ -259,7 +257,7 @@ The generated program can now be serialized and included in the blueprint. | ||||||
| ### Plutus Core Signposting | ### Plutus Core Signposting | ||||||
| 
 | 
 | ||||||
| All this fuss is to get us to a point where we can write Uplc - and good Uplc at that.  | All this fuss is to get us to a point where we can write Uplc - and good Uplc at that.  | ||||||
| Note that there's many ways to generate code and most of them are bad.   | Note that there are many ways to generate code and most of them are bad.   | ||||||
| The various design decisions and compilation steps make more sense  | The various design decisions and compilation steps make more sense  | ||||||
| when we have a better understanding of the target language.  | when we have a better understanding of the target language.  | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 waalge
						waalge