UNPKG

ton-assembly

Version:

TON assembler and disassembler

979 lines 31.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fIF = exports.fTHROWIFNOT = exports.fTHROWIF = exports.fTHROW = exports.fPREPAREDICT = exports.fJMPDICT = exports.fCALLDICT = exports.fCALLXARGS = exports.fSDBEGINSQ = exports.fSDBEGINS = exports.fPUSHINTX = exports.fXCHG = exports.fSTSLICECONST = exports.fPUSHCONT = exports.fPUSHSLICE = exports.fPOP = exports.fPUSH = exports.fPUSHINT = void 0; const types_1 = require("./types"); const instr_1 = require("./instr"); const util_1 = require("./util"); const builder_1 = require("./builder"); const compile_1 = require("./compile"); const fits = (val, bits) => val >= BigInt(-Math.pow(2, bits - 1)) && val <= BigInt(Math.pow(2, bits - 1) - 1); exports.fPUSHINT = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "PUSHINT_4" || loaded.$ === "PUSHINT_8" || loaded.$ === "PUSHINT_16") { return { $: "fPUSHINT", arg0: BigInt(loaded.arg0), loc: loaded.loc, }; } if (loaded.$ === "PUSHINT_LONG") { return { $: "fPUSHINT", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected PUSHINT variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg >= -5 && arg <= 10) { types_1.PUSHINT_4.store(b, { ...val, arg0: Number(arg), $: "PUSHINT_4", }, options); return; } if (fits(arg, 8)) { types_1.PUSHINT_8.store(b, { ...val, arg0: Number(arg), $: "PUSHINT_8", }, options); return; } if (fits(arg, 16)) { types_1.PUSHINT_16.store(b, { ...val, arg0: Number(arg), $: "PUSHINT_16", }, options); return; } types_1.PUSHINT_LONG.store(b, { ...val, $: "PUSHINT_LONG", }, options); }, }; exports.fPUSH = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "PUSH" || loaded.$ === "PUSH_LONG") { return { $: "fPUSH", arg0: loaded.arg0, kind: "stack", loc: loaded.loc, }; } if (loaded.$ === "PUSHCTR") { return { $: "fPUSH", arg0: loaded.arg0, kind: "control", loc: loaded.loc, }; } throw new Error("unexpected PUSH variant"); }, store: (b, val, options) => { const arg = val.arg0; if (val.kind === "control") { types_1.PUSHCTR.store(b, { ...val, arg0: arg, $: "PUSHCTR", }, options); return; } if (arg < 16) { types_1.PUSH.store(b, { ...val, arg0: arg, $: "PUSH", }, options); return; } types_1.PUSH_LONG.store(b, { ...val, arg0: arg, $: "PUSH_LONG", }, options); }, }; exports.fPOP = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "POP" || loaded.$ === "POP_LONG") { return { $: "fPOP", arg0: loaded.arg0, kind: "stack", loc: loaded.loc, }; } if (loaded.$ === "POPCTR") { return { $: "fPOP", arg0: loaded.arg0, kind: "control", loc: loaded.loc, }; } throw new Error("unexpected POP variant"); }, store: (b, val, options) => { const arg = val.arg0; if (val.kind === "control") { types_1.POPCTR.store(b, { ...val, arg0: arg, $: "POPCTR", }, options); return; } if (arg < 16) { types_1.POP.store(b, { ...val, arg0: arg, $: "POP", }, options); return; } types_1.POP_LONG.store(b, { ...val, arg0: arg, $: "POP_LONG", }, options); }, }; exports.fPUSHSLICE = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "PUSHSLICE" || loaded.$ === "PUSHSLICE_REFS" || loaded.$ === "PUSHSLICE_LONG") { return { $: "fPUSHSLICE", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected PUSHSLICE variant"); }, store: (b, val, options) => { const arg = val.arg0; if (!b.canFit(val.arg0.remainingBits + 26)) { // cannot place slice and instruction inline types_1.PUSHREFSLICE.store(b, { ...val, arg0: (0, util_1.rawCode)(arg), $: "PUSHREFSLICE", }, options); return; } if (arg.remainingRefs === 0 && arg.remainingBits <= 123) { types_1.PUSHSLICE.store(b, { ...val, arg0: arg, $: "PUSHSLICE", }, options); return; } if (arg.remainingRefs > 0 && arg.remainingRefs < 3 && arg.remainingBits <= 248) { types_1.PUSHSLICE_REFS.store(b, { ...val, arg0: arg, $: "PUSHSLICE_REFS", }, options); return; } types_1.PUSHSLICE_LONG.store(b, { ...val, arg0: arg, $: "PUSHSLICE_LONG", }, options); }, }; exports.fPUSHCONT = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "PUSHCONT" || loaded.$ === "PUSHCONT_SHORT" || loaded.$ === "PUSHREFCONT") { return { $: "fPUSHCONT", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected PUSHCONT variant"); }, store: (b, val, options) => { const arg = val.arg0; const b2 = new builder_1.CodeBuilder(); if (arg.$ === "Instructions") { (0, compile_1.compileInstructions)(b2, arg.instructions, options); } else { (0, util_1.codeSlice)((0, util_1.uint)(2), (0, util_1.uint)(7)).store(b2, arg, options); } const codeAsSlice = b2.asSlice(); if (!b.canFit(codeAsSlice.remainingBits)) { types_1.PUSHREFCONT.store(b, { ...val, arg0: arg, $: "PUSHREFCONT", }, options); return; } if (codeAsSlice.remainingRefs === 0 && codeAsSlice.remainingBits <= 120) { types_1.PUSHCONT_SHORT.store(b, { ...val, arg0: arg, $: "PUSHCONT_SHORT", }, options); return; } types_1.PUSHCONT.store(b, { ...val, arg0: arg, $: "PUSHCONT", }, options); }, }; exports.fSTSLICECONST = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "STSLICECONST") { return { $: "fSTSLICECONST", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected STSLICECONST variant"); }, store: (b, val, options) => { const arg = val.arg0; if (!b.canFit(arg.remainingBits + 22)) { // cannot place slice and instruction inline exports.fPUSHSLICE.store(b, { ...val, $: "fPUSHSLICE", }, options); types_1.STSLICER.store(b, { $: "STSLICER", loc: val.loc, }, options); return; } if (arg.remainingBits <= 57 && arg.remainingRefs <= 3) { types_1.STSLICECONST.store(b, { ...val, $: "STSLICECONST", }, options); return; } exports.fPUSHSLICE.store(b, { ...val, $: "fPUSHSLICE", }, options); types_1.STSLICER.store(b, { $: "STSLICER", loc: val.loc, }, options); }, }; exports.fXCHG = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "XCHG_0I" || loaded.$ === "XCHG_0I_LONG") { return { $: "fXCHG", arg0: 0, arg1: loaded.arg0, loc: loaded.loc, }; } if (loaded.$ === "XCHG_IJ") { return { $: "fXCHG", arg0: loaded.arg0, arg1: loaded.arg1, loc: loaded.loc, }; } throw new Error("unexpected XCHG variant"); }, store: (b, val, options) => { const i = val.arg0; const j = val.arg1; if (i === j) { // XCHG with same indices = NOP, just return return; } // Ensure i <= j for consistent representation const [arg0, arg1] = i <= j ? [i, j] : [j, i]; if (arg1 === 0) { // One of arguments is 0, this shouldn't happen in normalized form throw new Error("XCHG with zero argument should use XCHG_0I"); } if (arg0 === 0) { // XCHG s0, sj if (arg1 < 16) { types_1.XCHG_0I.store(b, { ...val, arg0: arg1, $: "XCHG_0I", }, options); return; } else { types_1.XCHG_0I_LONG.store(b, { ...val, arg0: arg1, $: "XCHG_0I_LONG", }, options); return; } } // XCHG s1 s2 if (arg0 === 1 && arg1 < 16) { types_1.XCHG_1I.store(b, { ...val, arg0: arg0, arg1: arg1, $: "XCHG_1I", }, options); return; } // XCHG si, sj where both i,j > 0 if (arg0 < 16 && arg1 < 16) { types_1.XCHG_IJ.store(b, { ...val, arg0: arg0, arg1: arg1, $: "XCHG_IJ", }, options); return; } // For large indices, we need to emit multiple XCHG_0I_LONG operations // XCHG si, sj = XCHG_0I_LONG si, XCHG_0I_LONG sj, XCHG_0I_LONG si types_1.XCHG_0I_LONG.store(b, { $: "XCHG_0I_LONG", arg0: arg0, loc: val.loc, }, options); types_1.XCHG_0I_LONG.store(b, { $: "XCHG_0I_LONG", arg0: arg1, loc: val.loc, }, options); types_1.XCHG_0I_LONG.store(b, { $: "XCHG_0I_LONG", arg0: arg0, loc: val.loc, }, options); }, }; const pow2Decomp = (n) => { let powers = 0; let remainder = n; while (remainder % 2n === 0n && remainder !== 0n) { powers++; remainder = remainder / 2n; } return [remainder, powers]; }; exports.fPUSHINTX = { load: s => { // This will load any PUSHINT* variant and convert to fPUSHINTX const loaded = instr_1.instr.load(s); if (loaded.$ === "PUSHINT_4" || loaded.$ === "PUSHINT_8" || loaded.$ === "PUSHINT_16" || loaded.$ === "PUSHINT_LONG" || loaded.$ === "PUSHPOW2" || loaded.$ === "PUSHNEGPOW2" || loaded.$ === "PUSHPOW2DEC") { return { $: "fPUSHINTX", arg0: loaded.$ === "PUSHINT_LONG" ? loaded.arg0 : BigInt(loaded.arg0), loc: loaded.loc, }; } throw new Error("unexpected PUSHINTX variant"); }, store: (b, val, options) => { const arg = val.arg0; // Try power of 2 optimizations first const [remainder, powers] = pow2Decomp(arg); if (remainder === 1n && arg > 0n) { // arg = 2^powers if (arg === 256n) { throw new Error("use PUSHNAN instead of 256 PUSHPOW2"); } types_1.PUSHPOW2.store(b, { ...val, arg0: powers, $: "PUSHPOW2", }, options); return; } if (remainder === -1n) { // arg = -2^powers types_1.PUSHNEGPOW2.store(b, { ...val, arg0: powers, $: "PUSHNEGPOW2", }, options); return; } // Try 2^n - 1 optimization const plusOne = arg + 1n; const [remainderPlus, powersPlus] = pow2Decomp(plusOne); if (remainderPlus === 1n) { types_1.PUSHPOW2DEC.store(b, { ...val, arg0: powersPlus, $: "PUSHPOW2DEC", }, options); return; } // Check if it fits in 8 bits (basic PUSHINT logic) if (fits(arg, 8)) { exports.fPUSHINT.store(b, { ...val, $: "fPUSHINT", }, options); return; } // For large powers, use PUSHINT + LSHIFT if (powers >= 20) { exports.fPUSHINT.store(b, { ...val, arg0: remainder, $: "fPUSHINT", }, options); types_1.LSHIFT.store(b, { $: "LSHIFT", arg0: powers, loc: val.loc, }, options); return; } // Fall back to regular PUSHINT exports.fPUSHINT.store(b, { ...val, $: "fPUSHINT", }, options); }, }; exports.fSDBEGINS = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "SDBEGINSX") { // This is a fallback case, we don't have the original slice throw new Error("Cannot load SDBEGINSX back to fSDBEGINS"); } throw new Error("unexpected SDBEGINS variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg.remainingRefs > 0) { throw new Error("no references allowed in slice"); } const sliceBits = arg.remainingBits; const opcodeBits = Math.floor((sliceBits + 5) / 8) * 8 + 3 + 21; if (b.canFit(opcodeBits)) { types_1.SDBEGINS.store(b, { ...val, $: "SDBEGINS", }, options); } else { exports.fPUSHSLICE.store(b, { ...val, $: "fPUSHSLICE", }, options); types_1.SDBEGINSX.store(b, { $: "SDBEGINSX", loc: val.loc, }, options); } }, }; exports.fSDBEGINSQ = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "SDBEGINSXQ") { // This is a fallback case, we don't have the original slice throw new Error("Cannot load SDBEGINSXQ back to fSDBEGINSQ"); } throw new Error("unexpected SDBEGINSQ variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg.remainingRefs > 0) { throw new Error("no references allowed in slice"); } const sliceBits = arg.remainingBits; const opcodeBits = Math.floor((sliceBits + 5) / 8) * 8 + 3 + 21; if (b.canFit(opcodeBits)) { types_1.SDBEGINSQ.store(b, { ...val, $: "SDBEGINSQ", }, options); } else { exports.fPUSHSLICE.store(b, { ...val, $: "fPUSHSLICE", }, options); types_1.SDBEGINSXQ.store(b, { $: "SDBEGINSXQ", loc: val.loc, }, options); } }, }; exports.fCALLXARGS = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "CALLXARGS" || loaded.$ === "CALLXARGS_1") { return { $: "fCALLXARGS", arg0: loaded.arg0, arg1: loaded.arg1, loc: loaded.loc, }; } throw new Error("unexpected CALLXARGS variant"); }, store: (b, val, options) => { if (val.arg1 === -1) { types_1.CALLXARGS.store(b, { ...val, $: "CALLXARGS", }, options); } else { types_1.CALLXARGS_1.store(b, { ...val, $: "CALLXARGS_1", }, options); } }, }; exports.fCALLDICT = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "CALLDICT" || loaded.$ === "CALLDICT_LONG") { return { $: "fCALLDICT", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected CALLDICT variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg >= 0 && arg <= 255) { types_1.CALLDICT.store(b, { ...val, $: "CALLDICT", }, options); } else if (arg >= 0 && arg <= 16383) { types_1.CALLDICT_LONG.store(b, { ...val, $: "CALLDICT_LONG", }, options); } else { exports.fPUSHINT.store(b, { $: "fPUSHINT", arg0: BigInt(arg), loc: val.loc, }, options); types_1.PUSHCTR.store(b, { $: "PUSHCTR", arg0: 3, loc: val.loc, }, options); types_1.EXECUTE.store(b, { $: "EXECUTE", loc: val.loc, }, options); } }, }; exports.fJMPDICT = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "JMPDICT") { return { $: "fJMPDICT", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected JMPDICT variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg >= 0 && arg <= 16383) { types_1.JMPDICT.store(b, { ...val, $: "JMPDICT", }, options); } else { exports.fPUSHINT.store(b, { $: "fPUSHINT", arg0: BigInt(arg), loc: val.loc, }, options); types_1.PUSHCTR.store(b, { $: "PUSHCTR", arg0: 3, loc: val.loc, }, options); types_1.JMPX.store(b, { $: "JMPX", loc: val.loc, }, options); } }, }; exports.fPREPAREDICT = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "PREPAREDICT") { return { $: "fPREPAREDICT", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected PREPAREDICT variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg >= 0 && arg <= 16383) { types_1.PREPAREDICT.store(b, { ...val, $: "PREPAREDICT", }, options); } else { exports.fPUSHINT.store(b, { $: "fPUSHINT", arg0: BigInt(arg), loc: val.loc, }, options); types_1.PUSHCTR.store(b, { $: "PUSHCTR", arg0: 3, loc: val.loc, }, options); } }, }; exports.fTHROW = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "THROW_SHORT" || loaded.$ === "THROW") { return { $: "fTHROW", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected THROW variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg >= 0 && arg <= 63) { types_1.THROW_SHORT.store(b, { ...val, $: "THROW_SHORT", }, options); } else if (arg >= 0 && arg <= 2047) { types_1.THROW.store(b, { ...val, $: "THROW", }, options); } else { throw new Error("THROW argument out of range"); } }, }; exports.fTHROWIF = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "THROWIF_SHORT" || loaded.$ === "THROWIF") { return { $: "fTHROWIF", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected THROWIF variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg >= 0 && arg <= 63) { types_1.THROWIF_SHORT.store(b, { ...val, $: "THROWIF_SHORT", }, options); } else if (arg >= 0 && arg <= 2047) { types_1.THROWIF.store(b, { ...val, $: "THROWIF", }, options); } else { throw new Error("THROWIF argument out of range"); } }, }; exports.fTHROWIFNOT = { load: s => { const loaded = instr_1.instr.load(s); if (loaded.$ === "THROWIFNOT_SHORT" || loaded.$ === "THROWIFNOT") { return { $: "fTHROWIFNOT", arg0: loaded.arg0, loc: loaded.loc, }; } throw new Error("unexpected THROWIFNOT variant"); }, store: (b, val, options) => { const arg = val.arg0; if (arg >= 0 && arg <= 63) { types_1.THROWIFNOT_SHORT.store(b, { $: "THROWIFNOT_SHORT", arg0: arg, loc: val.loc, }, options); } else if (arg >= 0 && arg <= 2047) { types_1.THROWIFNOT.store(b, { $: "THROWIFNOT", arg0: arg, loc: val.loc, }, options); } else { throw new Error("THROWIFNOT argument out of range"); } }, }; const isCodeEmpty = (code) => { if (code.$ === "Instructions") { return code.instructions.length === 0; } return code.slice.remainingBits === 0 && code.slice.remainingRefs === 0; }; const canCodeFit = (b, code, options) => { if (isCodeEmpty(code)) { return true; } const b2 = new builder_1.CodeBuilder(); if (code.$ === "Instructions") { (0, compile_1.compileInstructions)(b2, code.instructions, options); } else { (0, util_1.codeSlice)((0, util_1.uint)(2), (0, util_1.uint)(7)).store(b2, code, options); } const codeAsSlice = b2.asSlice(); return b.canFit(codeAsSlice.remainingBits + 16) && b.refs + codeAsSlice.remainingRefs <= 4; }; const canCodesFit = (b, code, code2, options) => { const b2 = new builder_1.CodeBuilder(); if (code.$ === "Instructions") { (0, compile_1.compileInstructions)(b2, code.instructions, options); } else { (0, util_1.codeSlice)((0, util_1.uint)(2), (0, util_1.uint)(7)).store(b2, code, options); } if (code2.$ === "Instructions") { (0, compile_1.compileInstructions)(b2, code2.instructions, options); } else { (0, util_1.codeSlice)((0, util_1.uint)(2), (0, util_1.uint)(7)).store(b2, code2, options); } const codeAsSlice = b2.asSlice(); return b.canFit(codeAsSlice.remainingBits + 16) && b.refs + codeAsSlice.remainingRefs <= 4; }; const canCodeFitAsRef = (b) => { // Check if we have space for reference (16 + 1 bits for ref header) return b.canFit(17); }; exports.fIF = { load: _s => { throw new Error("fIF load is not implemented - use specific IF instructions"); }, store: (b, val, options) => { const { kind, trueBranch, falseBranch, loc } = val; if (kind === "IFELSE") { if (!falseBranch) { throw new Error("IFELSE requires falseBranch"); } // Handle IFELSE with complex logic from IFELSE-cont2 const trueEmpty = isCodeEmpty(trueBranch); const falseEmpty = isCodeEmpty(falseBranch); if (trueEmpty) { // Simplify to IFNOT exports.fIF.store(b, { ...val, kind: "IFNOT", trueBranch: falseBranch, falseBranch: undefined }, options); return; } if (falseEmpty) { // Simplify to IF exports.fIF.store(b, { ...val, kind: "IF", falseBranch: undefined }, options); return; } const trueFits = canCodeFit(b, trueBranch, options); const falseFits = canCodeFit(b, falseBranch, options); const bothFits = canCodesFit(b, trueBranch, falseBranch, options); if (bothFits) { // Both fit inline - use PUSHCONT + PUSHCONT + IFELSE exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: trueBranch, loc }, options); exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: falseBranch, loc }, options); types_1.IFELSE.store(b, { $: "IFELSE", loc }, options); return; } if (trueFits && falseFits && canCodeFitAsRef(b)) { exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: falseBranch, loc }, options); types_1.IFREFELSE.store(b, { $: "IFREFELSE", arg0: trueBranch, loc, }, options); return; } if (!trueFits && falseFits && canCodeFitAsRef(b)) { // True as ref, false inline - use IFREFELSE // falseBranch will be inline, trueBranch as ref exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: falseBranch, loc }, options); types_1.IFREFELSE.store(b, { $: "IFREFELSE", arg0: trueBranch, loc, }, options); return; } if (trueFits && !falseFits && canCodeFitAsRef(b)) { // True inline, false as ref - use IFELSEREF // trueBranch will be inline, falseBranch as ref exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: trueBranch, loc }, options); types_1.IFELSEREF.store(b, { $: "IFELSEREF", arg0: falseBranch, loc, }, options); return; } if (!trueFits && !falseFits && canCodeFitAsRef(b) && canCodeFitAsRef(b)) { // Both as refs - use IFREFELSEREF types_1.IFREFELSEREF.store(b, { $: "IFREFELSEREF", arg0: trueBranch, arg1: falseBranch, loc, }, options); return; } // PSEUDO_PUSHREF_ALWAYS.store( // b, // PSEUDO_PUSHREF(code([{$: "fIF", kind, trueBranch, falseBranch, loc}])), // options, // ) exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: trueBranch, loc }, options); exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: falseBranch, loc }, options); types_1.IFELSE.store(b, { $: "IFELSE", loc }, options); return; } // Handle simple IF/IFNOT/IFJMP/IFNOTJMP const isEmpty = isCodeEmpty(trueBranch); if (isEmpty) { // Empty continuation optimizations if (kind === "IFJMP") { types_1.IFRET.store(b, { $: "IFRET", loc }, options); return; } if (kind === "IFNOTJMP") { types_1.IFNOTRET.store(b, { $: "IFNOTRET", loc }, options); return; } // For IF/IFNOT with empty continuation, do nothing return; } const fits = canCodeFit(b, trueBranch, options); if (fits) { // Fits inline - use PUSHCONT + instruction exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: trueBranch, loc }, options); switch (kind) { case "IF": types_1.IF.store(b, { $: "IF", loc }, options); break; case "IFNOT": types_1.IFNOT.store(b, { $: "IFNOT", loc }, options); break; case "IFJMP": types_1.IFJMP.store(b, { $: "IFJMP", loc }, options); break; case "IFNOTJMP": types_1.IFNOTJMP.store(b, { $: "IFNOTJMP", loc }, options); break; } } else if (canCodeFitAsRef(b)) { // Use reference version switch (kind) { case "IF": types_1.IFREF.store(b, { $: "IFREF", arg0: trueBranch, loc }, options); break; case "IFNOT": types_1.IFNOTREF.store(b, { $: "IFNOTREF", arg0: trueBranch, loc }, options); break; case "IFJMP": types_1.IFJMPREF.store(b, { $: "IFJMPREF", arg0: trueBranch, loc }, options); break; case "IFNOTJMP": types_1.IFNOTJMPREF.store(b, { $: "IFNOTJMPREF", arg0: trueBranch, loc }, options); break; } } else { // Fallback - force PUSHCONT even if it doesn't fit well exports.fPUSHCONT.store(b, { $: "fPUSHCONT", arg0: trueBranch, loc }, options); switch (kind) { case "IF": types_1.IF.store(b, { $: "IF", loc }, options); break; case "IFNOT": types_1.IFNOT.store(b, { $: "IFNOT", loc }, options); break; case "IFJMP": types_1.IFJMP.store(b, { $: "IFJMP", loc }, options); break; case "IFNOTJMP": types_1.IFNOTJMP.store(b, { $: "IFNOTJMP", loc }, options); break; } } }, }; //# sourceMappingURL=fift-instr.js.map