Skip to content

Commit

Permalink
handleLetted wiht delayHoisted == true
Browse files Browse the repository at this point in the history
  • Loading branch information
michele-nuzzi committed Oct 26, 2024
1 parent a3d65b9 commit 9939a82
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 68 deletions.
4 changes: 2 additions & 2 deletions packages/onchain/src/IR/IRNodes/IRFunc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -40,7 +40,7 @@ describe("compileIRToUPLC", () => {
);

// const beforeTree = prettyIRJsonStr( irTree );
handleLetted( irTree );
irTree = handleLettedAndReturnRoot( irTree );

expect(
irTree.toJson()
Expand Down Expand Up @@ -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(
Expand All @@ -95,7 +95,7 @@ describe("compileIRToUPLC", () => {
(tree as any).body.arg.hash
);

handleLetted( tree )
tree = handleLettedAndReturnRoot( tree )

expect( tree.toJson() )
.toEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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]]))))]"
)

})
Expand Down
12 changes: 5 additions & 7 deletions packages/onchain/src/IR/toUPLC/compileIRToUPLC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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(
Expand All @@ -113,7 +111,7 @@ export function compileIRToUPLC(
)
)
{
handleLetted( term );
term = handleLettedAndReturnRoot( term );
term = handleHoistedAndReturnRoot( term );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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", () => {

Expand Down Expand Up @@ -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,
Expand Down
145 changes: 114 additions & 31 deletions packages/onchain/src/IR/toUPLC/subRoutines/handleLetted/index.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 );
Expand All @@ -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 {
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
);
Expand Down
Loading

0 comments on commit 9939a82

Please sign in to comment.