From 9939a822d19f060c07e0ea948f1e984e96a7a76f Mon Sep 17 00:00:00 2001 From: Michele Nuzzi Date: Sat, 26 Oct 2024 19:23:23 +0200 Subject: [PATCH] handleLetted wiht delayHoisted == true --- packages/onchain/src/IR/IRNodes/IRFunc.ts | 4 +- .../toUPLC/__tests__/compileIR.letted.test.ts | 8 +- .../__tests__/compileIR.natives.test.ts | 5 +- .../onchain/src/IR/toUPLC/compileIRToUPLC.ts | 12 +- .../__tests__/IR.compile.handleLetted.test.ts | 8 +- .../toUPLC/subRoutines/handleLetted/index.ts | 145 ++++++++++++++---- .../subRoutines/replaceHoistedWithLetted.ts | 2 +- .../PStruct.pmatch.getElemAtTerm.test.ts | 37 +++-- 8 files changed, 153 insertions(+), 68 deletions(-) diff --git a/packages/onchain/src/IR/IRNodes/IRFunc.ts b/packages/onchain/src/IR/IRNodes/IRFunc.ts index 7df2ac95..cf65c9d8 100644 --- a/packages/onchain/src/IR/IRNodes/IRFunc.ts +++ b/packages/onchain/src/IR/IRNodes/IRFunc.ts @@ -112,8 +112,8 @@ export class IRFunc readonly meta: IRFuncMetadata get name(): string | undefined { return this.meta.name }; - private _parent: IRTerm | undefined; - get parent(): IRTerm | undefined { return this._parent; } + private _parent: IRParentTerm | undefined; + get parent(): IRParentTerm | undefined { return this._parent; } set parent( newParent: IRTerm | undefined ) { if(!( // assert diff --git a/packages/onchain/src/IR/toUPLC/__tests__/compileIR.letted.test.ts b/packages/onchain/src/IR/toUPLC/__tests__/compileIR.letted.test.ts index 37267ced..ef630338 100644 --- a/packages/onchain/src/IR/toUPLC/__tests__/compileIR.letted.test.ts +++ b/packages/onchain/src/IR/toUPLC/__tests__/compileIR.letted.test.ts @@ -5,7 +5,7 @@ import { IRFunc } from "../../IRNodes/IRFunc"; import { IRLetted } from "../../IRNodes/IRLetted"; import { IRVar } from "../../IRNodes/IRVar"; import { IRTerm } from "../../IRTerm"; -import { handleLetted } from "../subRoutines/handleLetted/index"; +import { handleLettedAndReturnRoot } from "../subRoutines/handleLetted/index"; import { _ir_apps } from "../../tree_utils/_ir_apps"; import { prettyIRJsonStr } from "../../utils/showIR"; @@ -40,7 +40,7 @@ describe("compileIRToUPLC", () => { ); // const beforeTree = prettyIRJsonStr( irTree ); - handleLetted( irTree ); + irTree = handleLettedAndReturnRoot( irTree ); expect( irTree.toJson() @@ -68,7 +68,7 @@ describe("compileIRToUPLC", () => { test("same scope; different DeBruijn", () => { // we use `IRDelayed` because plain `IRVars`a re inlined - const tree = new IRFunc( + let tree: IRTerm = new IRFunc( 1, new IRApp( new IRFunc( @@ -95,7 +95,7 @@ describe("compileIRToUPLC", () => { (tree as any).body.arg.hash ); - handleLetted( tree ) + tree = handleLettedAndReturnRoot( tree ) expect( tree.toJson() ) .toEqual( diff --git a/packages/onchain/src/IR/toUPLC/__tests__/compileIR.natives.test.ts b/packages/onchain/src/IR/toUPLC/__tests__/compileIR.natives.test.ts index aa7eb71f..e4d5de8b 100644 --- a/packages/onchain/src/IR/toUPLC/__tests__/compileIR.natives.test.ts +++ b/packages/onchain/src/IR/toUPLC/__tests__/compileIR.natives.test.ts @@ -51,7 +51,7 @@ describe("compileIRToUPLC", () => { const uplc = compileIRToUPLC( IRNative._matchList ); const expectedUplcStr = - "[(lam a [(lam b [(lam c (lam d (lam e (lam f (force [[[c f] d] (delay [[e [b f]] [a f]])]))))) (force (force (builtin chooseList)))]) (force (builtin headList))]) (force (builtin tailList))]"; + "[(lam a [(lam b [(lam c (lam d (lam e (lam f (force (force [[[c f] d] (delay [[e [b f]] [a f]])])))))) (force (force (builtin chooseList)))]) (force (builtin headList))]) (force (builtin tailList))]"; // "(lam a (lam b (lam c (force [[[(force (force (builtin chooseList))) c] a] (delay [[b [(force (builtin headList)) c]] [(force (builtin tailList)) c]])]))))"; expect( @@ -70,7 +70,8 @@ describe("compileIRToUPLC", () => { expect( showUPLC( uplc ) ) .toEqual( - "[(lam a [(lam b [a (lam c [[b b] c])]) (lam b [a (lam c [[b b] c])])]) (lam a (lam b (lam c (lam d [(lam e [[[(lam f (lam g (lam h (force [[[(force (force (builtin chooseList))) h] f] (delay [[g [(force (builtin headList)) h]] [(force (builtin tailList)) h]])])))) [b e]] [c e]] d]) [[a b] c]]))))]" + "[(lam a [(lam b [(lam c (lam d (lam e [(lam f [f f]) (lam f (lam g [[[(lam h (lam i (lam l (force (force [[[c l] h] (delay [[i [b l]] [a l]])]))))) [d [f f]]] [e [f f]]] g]))]))) (force (force (builtin chooseList)))]) (force (builtin headList))]) (force (builtin tailList))]" + // "[(lam a [(lam b [a (lam c [[b b] c])]) (lam b [a (lam c [[b b] c])])]) (lam a (lam b (lam c (lam d [(lam e [[[(lam f (lam g (lam h (force [[[(force (force (builtin chooseList))) h] f] (delay [[g [(force (builtin headList)) h]] [(force (builtin tailList)) h]])])))) [b e]] [c e]] d]) [[a b] c]]))))]" ) }) diff --git a/packages/onchain/src/IR/toUPLC/compileIRToUPLC.ts b/packages/onchain/src/IR/toUPLC/compileIRToUPLC.ts index e2ed36ae..74915c20 100644 --- a/packages/onchain/src/IR/toUPLC/compileIRToUPLC.ts +++ b/packages/onchain/src/IR/toUPLC/compileIRToUPLC.ts @@ -7,7 +7,7 @@ import { _modifyChildFromTo } from "./_internal/_modifyChildFromTo"; import { _makeAllNegativeNativesHoisted } from "./_internal/_makeAllNegativeNativesHoisted"; import { _irToUplc } from "./_internal/_irToUplc"; import { includesNode } from "./_internal/includesNode"; -import { handleLetted } from "./subRoutines/handleLetted"; +import { handleLettedAndReturnRoot } from "./subRoutines/handleLetted"; import { handleHoistedAndReturnRoot } from "./subRoutines/handleHoistedAndReturnRoot"; import { replaceNativesAndReturnRoot } from "./subRoutines/replaceNatives"; import { replaceClosedLettedWithHoisted } from "./subRoutines/replaceClosedLettedWithHoisted"; @@ -97,11 +97,9 @@ export function compileIRToUPLC( // handle letted before hoisted because the tree is smaller // and we also have less letted dependecies to handle - handleLetted( term ); - if( options.delayHoists ) - { - term = handleHoistedAndReturnRoot( term ); - } + term = handleLettedAndReturnRoot( term ); + + term = handleHoistedAndReturnRoot( term ); // replaced hoisted terms might include new letted terms while( @@ -113,7 +111,7 @@ export function compileIRToUPLC( ) ) { - handleLetted( term ); + term = handleLettedAndReturnRoot( term ); term = handleHoistedAndReturnRoot( term ); } diff --git a/packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/__tests__/IR.compile.handleLetted.test.ts b/packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/__tests__/IR.compile.handleLetted.test.ts index dc5fa8c5..7361e689 100644 --- a/packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/__tests__/IR.compile.handleLetted.test.ts +++ b/packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/__tests__/IR.compile.handleLetted.test.ts @@ -9,7 +9,7 @@ import { IRLetted, getLettedTerms } from "../../../../IRNodes/IRLetted"; import { IRNative } from "../../../../IRNodes/IRNative"; import { IRNativeTag } from "../../../../IRNodes/IRNative/IRNativeTag"; import { IRVar } from "../../../../IRNodes/IRVar"; -import { handleLetted } from ".."; +import { handleLettedAndReturnRoot } from ".."; import { IRTerm } from "../../../../IRTerm"; import { _ir_apps } from "../../../../tree_utils/_ir_apps"; import { IRHoisted } from "../../../../IRNodes/IRHoisted"; @@ -20,7 +20,7 @@ import { Machine } from "@harmoniclabs/plutus-machine"; import { pInt } from "../../../../../pluts/lib/std/int/pInt"; import { irHashToHex } from "../../../../IRHash"; -describe("handleLetted", () => { +describe("handleLettedAndReturnRoot", () => { test("single ref inlined", () => { @@ -413,11 +413,11 @@ describe("handleLetted", () => { ) ); - const newIR = quadrupleIR.clone(); + let newIR: IRTerm = quadrupleIR.clone(); // console.log( showIR( newIR ) ); - handleLetted( newIR ); + newIR = handleLettedAndReturnRoot( newIR ); const expected = new IRFunc( funcArity, diff --git a/packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/index.ts b/packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/index.ts index eb987b61..efc715e6 100644 --- a/packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/index.ts +++ b/packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/index.ts @@ -1,14 +1,14 @@ import { IRApp } from "../../../IRNodes/IRApp"; import { IRFunc } from "../../../IRNodes/IRFunc"; -import { getSortedLettedSet, getLettedTerms, IRLetted, jsonLettedSetEntry, expandedJsonLettedSetEntry, LettedSetEntry } from "../../../IRNodes/IRLetted"; +import { getSortedLettedSet, getLettedTerms, IRLetted } from "../../../IRNodes/IRLetted"; import { IRVar } from "../../../IRNodes/IRVar"; import { IRTerm } from "../../../IRTerm"; import { _addDepths } from "../../_internal/_addDepth"; import { _modifyChildFromTo } from "../../_internal/_modifyChildFromTo"; -import { findAll, findAllNoHoisted } from "../../_internal/findAll"; +import { findAllNoHoisted } from "../../_internal/findAll"; import { getDebruijnInTerm } from "../../_internal/getDebruijnInTerm"; -import { _getMinUnboundDbn, groupByScope } from "./groupByScope"; -import { lettedToStr, prettyIR, prettyIRJsonStr, prettyIRText, showIR } from "../../../utils/showIR"; +import { _getMinUnboundDbn } from "./groupByScope"; +import { prettyIRJsonStr } from "../../../utils/showIR"; import { IRDelayed } from "../../../IRNodes/IRDelayed"; import { IRForced } from "../../../IRNodes/IRForced"; import { lowestCommonAncestor } from "../../_internal/lowestCommonAncestor"; @@ -21,21 +21,18 @@ import { sanifyTree } from "../sanifyTree"; import { mapArrayLike } from "../../../IRNodes/utils/mapArrayLike"; import { IRCase } from "../../../IRNodes/IRCase"; import { IRConstr } from "../../../IRNodes/IRConstr"; -import { IRHoisted } from "../../../IRNodes/IRHoisted"; import { IRRecursive } from "../../../IRNodes/IRRecursive"; import { IRSelfCall } from "../../../IRNodes/IRSelfCall"; -import { IRNativeTag } from "../../../IRNodes/IRNative/IRNativeTag"; -import { IRNative } from "../../../IRNodes/IRNative"; import { findHighestRecursiveParent } from "./findHighestRecursiveParent"; -import { max } from "@harmoniclabs/bigint-utils"; +import { IRParentTerm } from "../../../utils/isIRParentTerm"; -export function handleLetted( term: IRTerm ): void +export function handleLettedAndReturnRoot( term: IRTerm ): IRTerm { // console.log(" ------------------------------------------- handleLetted ------------------------------------------- "); // console.log( prettyIRJsonStr( term )) // most of the time we are just compiling small // pre-execuded terms (hence constants) - if( term instanceof IRConst ) return; + if( term instanceof IRConst ) return term; // TODO: should probably merge `markRecursiveHoistsAsForced` inside `getLettedTerms` to iter once markRecursiveHoistsAsForced( term ); @@ -46,9 +43,8 @@ export function handleLetted( term: IRTerm ): void while( true ) { const allDirectLetted = getLettedTerms( term, { all: false, includeHoisted: false }); - if( allDirectLetted.length === 0 ) return; - // console.log("found ", allDirectLetted.length, "letted terms"); - + if( allDirectLetted.length === 0 ) return term; + const sortedLettedSet = getSortedLettedSet( allDirectLetted ); const { @@ -81,7 +77,20 @@ export function handleLetted( term: IRTerm ): void continue; } - const maxScope = findLettedMaxScope( letted ); + const maxScope = findLettedMaxScope( letted ) ?? ((): IRTerm => { + if( letted.meta.isClosed || letted.isClosedAtDbn( 0 ) ) + { + // value is closed (hoisted), + // so the max scope is the entire script + return term; + } + else throw new Error( + `could not find a max scope for letted value with hash ${irHashToHex(letted.hash)}` + ); + })(); + + const lettedTermCanBeHoisted = maxScope === term; + const minScope = findHighestRecursiveParent( letted, maxScope ); sanifyTree( maxScope ); @@ -155,34 +164,48 @@ export function handleLetted( term: IRTerm ): void if( !isIRTerm( lca ) ) { - // default to maxScope - // lca = maxScope; throw new Error( "letting nodes with hash " + irHashToHex( lettedHash ) + " from different trees" ); } + const realLca = lca; + // point to the first func or delay node above the lca // (worst case scenario we hit the maxScope; which is an IRFunc) // IRFuncs should always be under IRRecursives if any while(!( lca instanceof IRFunc || lca instanceof IRDelayed - )) + ) && lca ) { lca = lca?.parent ?? undefined; - // if somehow we hit the root - if( !isIRTerm( lca ) ) - { - throw new Error( - "lowest common ancestor outside the max scope" - ); - } + } + + + if( !isIRTerm( lca ) ) + { + if( !lettedTermCanBeHoisted ) + throw new Error( + "lowest common ancestor outside the max scope" + ); + + lca = realLca; + const tmpRoot = handleLettedAsHoistedAndReturnRoot( + letted, + realLca, // lca + sameLettedRefs, + term + ); + + if( lca === maxScope || !lca.parent ) term = tmpRoot; + + continue; } // shouldLog && console.log("lca", prettyIRJsonStr( lca ) ); - const parentNode: IRFunc | IRDelayed = lca; + const parentNode: IRTerm = lca; const parentNodeDirectChild = ( parentNode instanceof IRFunc || parentNode instanceof IRRecursive @@ -256,21 +279,18 @@ function isChildOf( child: IRTerm | undefined, parent: IRTerm ): boolean * @param letted * @returns {IRFunc} the lowest `IRFunc` in the tree that defines all the variables needed for the */ -function findLettedMaxScope( letted: IRLetted ): IRFunc | IRDelayed +function findLettedMaxScope( letted: IRLetted ): IRTerm | undefined { let minUnboundDbn = _getMinUnboundDbn( letted.value ); if( minUnboundDbn === undefined ) { let tmp: IRTerm | IRDelayed = letted; - let maxScope: IRTerm | IRDelayed | undefined = undefined; + let maxScope: IRTerm | undefined = undefined; while( tmp.parent ) { - tmp = tmp.parent + tmp = tmp.parent; if( tmp instanceof IRFunc || tmp instanceof IRDelayed ) maxScope = tmp; }; - if( !maxScope ) throw new Error( - `could not find a max scope for letted value with hash ${irHashToHex(letted.hash)}` - ); return maxScope; } @@ -424,4 +444,67 @@ function getDiffDbn( letted: IRLetted, parentNode: IRTerm ): number } } return diffDbn; +} + +function handleLettedAsHoistedAndReturnRoot( + letted: IRLetted, + lca: IRTerm, + sameLettedRefs: IRLetted[], + currentRoot: IRTerm +): IRTerm +{ + const lettedHash = letted.hash; + let parentNode: IRParentTerm | undefined = lca.parent; + const parentNodeDirectChild = lca; + + // add 1 to every var's DeBruijn that accesses stuff outside the parent node + // not including the `parentNode` node + // since the new function introdcued substituting the letted term + // is added inside the `parentNode` node + incrementUnboundDbns( + parentNodeDirectChild, + // `shouldNotModifyLetted` arg (given the hash returns `true` if it should NOT modify the term) + ({ hash }) => equalIrHash( hash, lettedHash ) + ); + + // now we replace + const lettedValue = letted.value; //.clone(); + + // no need to modify letted value dbns, since closed + // modifyValueToLetDbns( lettedValue, getDiffDbn( letted, parentNode ) ); + + const newNode = new IRApp( + new IRFunc( + 1, + parentNodeDirectChild + ), + lettedValue, + { __src__ : letted.meta.__src__ } + ); + + // replace child with new node + if( parentNode ) + { + _modifyChildFromTo( + parentNode, + parentNodeDirectChild, + newNode + ); + } + else + { + currentRoot = newNode; + } + + // console.log("replacing letted with value", prettyIRText( letted.value ) ) + for( const ref of sameLettedRefs ) + { + _modifyChildFromTo( + ref.parent, + ref, + new IRVar( getDebruijnInTerm( parentNodeDirectChild, ref ) ) + ); + } + + return currentRoot; } \ No newline at end of file diff --git a/packages/onchain/src/IR/toUPLC/subRoutines/replaceHoistedWithLetted.ts b/packages/onchain/src/IR/toUPLC/subRoutines/replaceHoistedWithLetted.ts index c17a8e66..090e0832 100644 --- a/packages/onchain/src/IR/toUPLC/subRoutines/replaceHoistedWithLetted.ts +++ b/packages/onchain/src/IR/toUPLC/subRoutines/replaceHoistedWithLetted.ts @@ -17,7 +17,7 @@ export function replaceHoistedWithLetted( term: IRTerm ): void { const parent = hoisted.parent!; const letted = new IRLetted( - Math.round( Number.MAX_SAFE_INTEGER / 2 ), + 0x0fffffff, // doesn't matter since closed term hoisted.hoisted, { isClosed: true } ); diff --git a/packages/onchain/src/pluts/PTypes/PStruct/__tests__/PStruct.pmatch.getElemAtTerm.test.ts b/packages/onchain/src/pluts/PTypes/PStruct/__tests__/PStruct.pmatch.getElemAtTerm.test.ts index 4a97b9d5..dd93de2c 100644 --- a/packages/onchain/src/pluts/PTypes/PStruct/__tests__/PStruct.pmatch.getElemAtTerm.test.ts +++ b/packages/onchain/src/pluts/PTypes/PStruct/__tests__/PStruct.pmatch.getElemAtTerm.test.ts @@ -84,23 +84,26 @@ describe("getElemAtTerm", () => { const unitAsData = toData( unit )( pmakeUnit() ); - const stuff = Struct3.Struct3({ - j: unitAsData, - k: unitAsData, - l: unitAsData, - m: Struct2.Struct2({ - f: unitAsData, - g: unitAsData, - h: unitAsData, - i: Struct1.Struct1({ - b: unitAsData, - a: unitAsData, - c: unitAsData, - d: unitAsData, - e: pDataI(42), - }) as any, - }) as any - }) + const stuff = plam( data, Struct3.type ) + ( unitAsData => + Struct3.Struct3({ + j: unitAsData, + k: unitAsData, + l: unitAsData, + m: Struct2.Struct2({ + f: unitAsData, + g: unitAsData, + h: unitAsData, + i: Struct1.Struct1({ + b: unitAsData, + a: unitAsData, + c: unitAsData, + d: unitAsData, + e: pDataI(42), + }) as any, + }) as any + }) + ).$( unitAsData ); test("extract nested", () => {