mlogjs / mlogjs

Compile javascript into Mindustry logic code (mlog)
https://mlogjs.github.io/mlogjs/
MIT License
69 stars 8 forks source link

Bad optimisation on Vars.time #230

Closed hsaturn closed 7 months ago

hsaturn commented 7 months ago

The optimisation there is not expected

var factor = 1000;
var last = Math.floor(Vars.time / factor);
while (Math.floor(Vars.time / factor) == last);
print "I want to arrive here but I cant";
printflush(message1);

See the infinite loop at line 3:

set factor:2:4 1000
op div &t0 @time factor:2:4
op floor last:3:4 &t0
jump 3 always  <------------ Yeeepeeee :-D
print "I wont arrive here"
printflush message1

By the way, the optimiser should have discarded the print and printflush if the loop were ok.

Best regards

hsaturn commented 7 months ago

Edit: self-answer, the unreleased version fixes this. Thanks again

Hello

It seems that the website does not reflect this change. I don't know which version of the website (should be a little enhancement to display it somewhere ?)

I've used this code as the previous one was a little buggy (!!)

var factor = 1000;
var message = getBuilding("message1");
var last = Math.floor(Vars.time / factor);
while (Math.floor(Vars.time / factor) == last);
print("I want to arrive here but I cant");
printFlush(message);

Mlogjs is really great !

JeanJPNM commented 7 months ago

The github pages website is updated on each release. To view the latest changes, you can checkout out the unreleased version

hsaturn commented 7 months ago

About the unreleased version. You may ignore this post since it's about unreleased (!). => let me know if such comment is useless of if you're happy with it.

I've seen that generated code is longer and a few slower than the stable one (my js gives 719 lines vs 693) *** Which may become a issue as asm is limited to 1000 lines Loss of speed on my js is about 1%

=> My guess is that optimisation of expression evaluation seems to be ok but suffers of spurious jumps as saw in this code (as already commented there => My second guess is that jump optimization once implemented will reduce the oversize.

var a;
var b;
while (true) {
    if (a == 0 && b == 1)
    {
        print("do some stuff");
    }
}
print("end");

Generated asm should be

op equal &t0 a:1:4 0
jump 0 equal &t0 0
op equal &t0 b:2:4 1
jump 0 equal &t0 0
print "do some stuff"
jump 0 always

Which becomes same number of rows of old version but running faster ! Anyway, I guess that the short-circuit optimisation could lead to larger code that won't fit in 1000 lines. => Having an option (if both generators are available) could help in this situation (optimize for size / speed).

My js has a fps counter, loop is 1.06s vs 1.05s when the processor is connected to

Thanks again for this very cool project !

Here is my js script

const version: String = "hyper-controller-20240325-4";
var v = version;    // First line of asm
const display_version = "[violet]" + version + "[white]";
var min_power = 0;
var message;
var keep_coal = 6500;
var keep_copper = 6500;
var keep_graphite = 6500;
var keep_lead = 8000;
var keep_sand = 6500;
var keep_tit = 6500;
var keep_pyr = 4080;
var keep_tho = 6500;
var keep_sil = 6500;
var keep_spore = 6500;
var keep_scrap = 6500;
var keep_phase = 5000;
var keep_surge = 6500;
var keep_blast = 6500;
var keep_glass = 6500;
var keep_plast = 6500;
var min_cryo = 25;

const need_color: String = "[cyan]";
const color_send: String = "[blue]";
const error = "[red]";
const warning = "[yellow]";
const white: String = "[white]";

var powSteam = 330;
var powCombustion = 60;
var powDifferential = 1080;

var n = "message";

var loop = 0;                // Loops incremental counter
var core_max_rsrc = 4000;    // Max resources of the core
var link_low_rsrc = 3990;    // Hyper link enable build rsrc if below this value
var core : AnyBuilding = undefined;

var copper, coal, glass, scrap, lead, sand, pyr, tit, tho, sil, graphite, plast, phase, surge, blast, spore;
var enough_lead, enough_blast, enough_plast, enough_glass, enough_copper, enough_surge, enough_phase, enough_graphite, enough_scrap, enough_coal, enough_sand, enough_tit, enough_pyr, enough_tho, enough_sil, enough_spore;
var enabled : Boolean;
var mass_enabled = false;

var pow_node : AnyBuilding = undefined;
var pow_stored = 0, pow_capacity = 0;
var impact_next_change = Vars.second;   // Next time to change state of impact reactor

// Automatic select item to send for launchPad
// will never go below keep_{rsrc}
var launch_item = undefined;
var sender: Boolean;   // True if we find a unloader (normally connected to launchPad)

var standalone_disass_tho_cryo;
var need_disass;
var power = -999;
var power_max = 0;  // max power ever seen

// memory cell link
var id = undefined;
var time = undefined;   // Last time we received something

var last_tick = Vars.tick;
var avg_ticks = 60;

while (true) {
    loop++;
    var ticks = Vars.tick - last_tick;
    last_tick = Vars.tick;
    avg_ticks = 0.7 * avg_ticks + 0.3 * ticks;

    var latency = avg_ticks / 60;
    latency = Math.floor(latency * 100) / 100;

    print(display_version, " #", id, "\nLoop ", loop, " / ", latency, "s\n");

    sender = false;
    if (core !== undefined) {
        copper = core.copper;
        coal = core.coal;
        lead = core.lead;
        sand = core.sand;
        glass = core.metaglass;
        pyr = core.pyratite;
        tit = core.titanium;
        tho = core.thorium;
        sil = core.silicon;
        scrap = core.scrap;
        graphite = core.graphite;
        plast = core.plastanium;
        phase = sensor(Items.phaseFabric, core);
        surge = sensor(Items.surgeAlloy, core);
        blast = sensor(Items.blastCompound, core);
        spore = sensor(Items.sporePod, core);

        enough_copper = copper > keep_copper;
        enough_lead = lead > keep_lead;
        enough_coal = coal > keep_coal;
        enough_sand = sand > keep_sand;
        enough_tit = tit > keep_tit;
        enough_pyr = pyr > keep_pyr;
        enough_tho = tho > keep_tho;
        enough_sil = sil > keep_sil;
        enough_spore = spore > keep_spore;
        enough_scrap = scrap > keep_scrap;
        enough_graphite = graphite > keep_graphite;
        enough_phase = phase > keep_phase;
        enough_surge = surge > keep_surge;
        enough_blast = blast > keep_blast;
        enough_glass = glass > keep_glass;
        enough_plast = plast > keep_plast;
    }
    else
    {
        enough_copper = true;
        enough_lead = true;
        enough_coal = true;
        enough_sand = true;
        enough_tit = true;
        enough_pyr = true;
        enough_tho = true;
        enough_sil = true;
        enough_spore = true;
        enough_scrap = true;
        enough_graphite = true;
        enough_phase = true;
        enough_surge = true;
        enough_blast = true;
        enough_glass = true;
        enough_plast = true;
    }
    var old_std_disass = standalone_disass_tho_cryo;
    standalone_disass_tho_cryo = 0;
    var power_left = power;
    for (let i = Vars.links - 1; i >= 0; i--) {
        var unhandled: Boolean = false;
        var link : AnyBuilding = getLink(i);
        var ltype = link.type;
        if (ltype == Blocks.thoriumReactor) {
            var enough_cryo = sensor(Liquids.cryofluid, link) >= min_cryo;
            enabled = (power_left < -15) && enough_cryo;
            // print("Tho enabled ", enabled, ", pleft ", power_left, ", enough_cryo ", enough_cryo, "\n");
            if (core === undefined) {
                var t: number = sensor(Items.thorium, link);
                var m = link.itemCapacity;
                standalone_disass_tho_cryo += m - t;
                if (!enough_cryo)
                    standalone_disass_tho_cryo = 1000;
                if (enabled && link.enabled == false)
                    power_left = power_left + 30 * t;
            }
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.powerNode || ltype == Blocks.powerNodeLarge) {
            pow_node = link;
        }
        else if (ltype == Blocks.disassembler) {
            if (core) {
                asm`control enabled ${link} ${need_disass} 0 0`;
            }
            else {
                enabled = old_std_disass > 1;
                asm`control enabled ${link} ${enabled} 0 0`;
            }
        }
        else if (ltype == Blocks.impactReactor) {
            // print("impact cryo: ", link.cryofluid, "\n");
            var cryo = link.cryofluid;
            print("cryo:", cryo, "\n");
            var capa = link.powerNetCapacity;
            if (cryo > 10) {
                enabled = (sensor(Items.blastCompound, link) > 5)
                    && ((pow_stored > 30000) || (capa == 0))
                    && (power_left < -200);
                print("impact ", capa, " power_left:", power_left, " pow_stored:", pow_stored, ":", enabled, "\n");

                if (enabled || (enabled != link.enabled) && (Vars.second > impact_next_change)) {
                    if (enabled)
                        impact_next_change = Vars.second + 60;
                    asm`control enabled ${link} ${enabled} 0 0`;
                    power_left = power_left - 6300;
                }
            }
            else
                asm`control enabled ${link} false 0 0`;

            print("Impact Power_left, ", power_left, " next:", Math.max(0, Math.floor(impact_next_change - Vars.second)), "\n");
        }
        else if (ltype == Blocks.steamGenerator
            || ltype == Blocks.combustionGenerator // 60 
            || ltype == Blocks.differentialGenerator) {
            var plink = powSteam;
            enabled = link.enabled;
            if (ltype == Blocks.combustionGenerator)
                plink = powCombustion;
            else if (ltype == Blocks.differentialGenerator)
                plink = powDifferential;
            if (power_left < -50) {
                enabled = true;
                if (link.enabled == false)
                    power_left += plink;
            }
            else {
                if (power >= plink && link.enabled) {
                    enabled = false;
                    power_left -= plink;
                    enabled = false;
                }
            }
            asm`control enabled ${link} ${enabled} 0 0`;
            // print("Power ", power_left, "/", power, ", link ", plink, " en=", enabled, "\n");
        }
        else if (ltype == Blocks.coalCentrifuge) {
            enabled = coal != core_max_rsrc;
            asm`control enabled ${link} ${enabled} 0 0`;
            if (link.oil == 0)
                print(warning + "Need oil in ", link, "\n" + white);
        }
        else if (ltype == Blocks.launchPad) {
            enabled = link.totalItems / link.itemCapacity >= link.progress;
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.message) {
            message = link;
        }
        else if (ltype == Blocks.coreShard) {
            core = link;
        }
        else if (ltype == Blocks.coreFoundation) {
            core = link;
        }
        else if (ltype == Blocks.coreNucleus) {
            core = link;
        }
        else if (ltype == Blocks.graphitePress || ltype == Blocks.multiPress) {
            if (graphite == core_max_rsrc)
                enabled = false;
            else
                enabled = enough_coal;
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.siliconSmelter || ltype == Blocks.siliconCrucible) {
            enabled = enough_coal && enough_sand;
            if (sil == core_max_rsrc)
                enabled = false;
            else if (ltype == Blocks.siliconCrucible)
                enabled = enabled && enough_pyr;
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.massDriver) {
            enabled = (mass_enabled && link.totalItems > 50) || (link.totalItems == 0);
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.memoryCell) {
            const memory = new Memory(link, 64);
            if (core) {
                print("Dumping to memory\n");
                id = 0;
                memory[0] = 65; // controller
                memory[1] = 0; // main controller
                memory[2] = core_max_rsrc;
                memory[3] = link_low_rsrc;
                memory[4] = Math.floor(Vars.time);
                memory[5] = min_power;
                memory[10] = copper;
                memory[11] = coal;
                memory[12] = lead;
                memory[13] = sand;
                memory[14] = glass;
                memory[15] = pyr;
                memory[16] = tit;
                memory[17] = tho;
                memory[18] = sil;
                memory[19] = scrap;
                memory[20] = graphite;
                memory[21] = plast;
                memory[22] = phase;
                memory[23] = surge;
                memory[24] = blast;
                memory[25] = spore;
            }
            else if (memory[0] == 65) {
                var cell_id = memory[1];
                var next_id = cell_id + 1;
                var last_update = Math.floor(Vars.time - memory[4]);
                // print("Last update: ", last_update, ", id=", id, "\n");
                if (id === undefined || id > next_id)
                    id = next_id;
                if (last_update > 10000) {
                    print(warning + "Resync, id=", id, white + "\n");
                    memory[0] = 0;  // data too old, trying to reconnect
                    id = undefined;
                }
                if (id != undefined && id > cell_id) {
                    print("Retrieving data from cell #", cell_id, ", id=", id, "\n");
                    core_max_rsrc = memory[2];
                    link_low_rsrc = memory[3];
                    time = Math.floor(Vars.time);
                    min_power = memory[5];
                    copper = memory[10];
                    coal = memory[11];
                    lead = memory[12];
                    sand = memory[13];
                    glass = memory[14];
                    pyr = memory[15];
                    tit = memory[16];
                    tho = memory[17];
                    sil = memory[18];
                    scrap = memory[19];
                    graphite = memory[20];
                    plast = memory[21];
                    phase = memory[22];
                    surge = memory[23];
                    blast = memory[24];
                    spore = memory[25];
                }
                if (id != undefined && id <= cell_id) {
                    print("Transmitting data to cell #", cell_id, ", id=", id, "\n");
                    memory[0] = 65;
                    memory[1] = id;
                    memory[2] = core_max_rsrc;
                    memory[3] = link_low_rsrc;
                    memory[4] = time;
                    memory[10] = copper;
                    memory[11] = coal;
                    memory[12] = lead;
                    memory[13] = sand;
                    memory[14] = glass;
                    memory[15] = pyr;
                    memory[16] = tit;
                    memory[17] = tho;
                    memory[18] = sil;
                    memory[19] = scrap;
                    memory[20] = graphite;
                    memory[21] = plast;
                    memory[22] = phase;
                    memory[23] = surge;
                    memory[24] = blast;
                    memory[25] = spore;
                }
            }
            else if (id !== undefined) {

                // Allocate this memory cell to this processor.
                // print("Allocating memory #", id, "\n");
                memory[0] = 65; // controller
                memory[1] = id; //
                memory[4] = Vars.time;
            }
        }
        else if (ltype == Blocks.kiln) {
            if (glass == core_max_rsrc)
                enabled = false;
            else
                enabled = enough_lead & enough_sand;
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.sporePress) {
            if (spore == core_max_rsrc)
                enabled = false;
            else {
                enabled = true;
                if (link.oil == 0)
                    print(warning + "Need oil in ", link, "\n" + white);
            }
        }
        else if (ltype == Blocks.plastaniumCompressor) {
            if (plast == core_max_rsrc)
                enabled = false;
            else {
                enabled = enough_tit;
                if (link.oil == 0)
                    print(warning + "Need oil in ", link, "\n" + white);
            }
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.phaseWeaver) {
            if (phase == core_max_rsrc)
                enabled = false;
            else
                enabled = enough_sand & enough_tho;
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.surgeSmelter) {
            if (surge == core_max_rsrc)
                enabled = false;
            else
                enabled = enough_copper & enough_lead & enough_tit & enough_sil;
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.pyratiteMixer) {
            if (pyr == core_max_rsrc)
                enabled = false;
            else
                enabled = enough_coal & enough_lead & enough_sand;
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.blastMixer) {
            if (blast == core_max_rsrc)
                enabled = false;
            else
                enabled = enough_pyr & enough_spore;
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.cultivator) {
            enabled = (spore < core_max_rsrc)
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else if (ltype == Blocks.unloader) {
            sender = true;
            if (launch_item !== undefined) {
                asm`control enabled ${link} true 0 0`;
                asm`control config ${link} ${launch_item} 0 0`;
            }
            else
                asm`control enabled ${link} false 0 0`;
        }
        else if (link.maxHealth > 2000) {
            unhandled = true;
            // Try to avoid power big failure by deactivating
            // tower that eats a lot
            var ammo = link.ammoCapacity;
            enabled = true;
            if (ammo == undefined)
                unhandled = true;
            else {
                unhandled = false;
                if (link.powerNetStored < 60000) {
                    print("[red]Avoiding loose of power for ", ltype, " pstored=", link.powerNetStored, "[white]\n");
                    enabled = false;
                }
            }
            asm`control enabled ${link} ${enabled} 0 0`;
        }
        else
            unhandled = true;
        if (unhandled)
            print(warning + "Not handled: ", link, "\n" + white);
    }
    if (sender) {
        launch_item = undefined;
        var count = 0;
        if (count < blast && enough_blast) { launch_item = Items.blastCompound; count = blast; }
        if (count < phase && enough_phase) { launch_item = Items.phaseFabric; count = phase; }
        if (count < surge && enough_surge) { launch_item = Items.surgeAlloy; count = surge; }
        if (count < tho && enough_tho) { launch_item = Items.thorium; count = tho; }
        if (count < tit && enough_tit) { launch_item = Items.titanium; count = tit; }
        if (count < spore && enough_spore) { launch_item = Items.sporePod; count = spore; }
        if (count < plast && enough_plast) { launch_item = Items.plastanium; count = plast; }
        if (count < pyr && enough_pyr) { launch_item = Items.pyratite; count = pyr; }
        if (count < sil && enough_sil) { launch_item = Items.silicon; count = sil; }
        if (count < scrap && enough_scrap) { launch_item = Items.scrap; count = scrap; }
        if (count < glass && enough_glass) { launch_item = Items.metaglass; count = glass; }
        if (count < lead && enough_lead) { launch_item = Items.lead; count = lead; }
        if (count < graphite && enough_graphite) { launch_item = Items.graphite; count = graphite; }
        if (count < coal && enough_coal) { launch_item = Items.coal; count = coal; }
        if (count < sand && enough_sand) { launch_item = Items.sand; count = sand; }
        if (count < copper && enough_copper) { launch_item = Items.copper; count = copper; }
        if (count) {
            print(color_send + "Sending ", launch_item, ":", count, "\n" + white);
        }
        else {
            print(color_send + "Unloader stopped\n" + white);
        }
    }

    if (pow_node) {

        pow_capacity = sensor(LAccess.powerNetCapacity, pow_node);
        pow_stored = sensor(LAccess.powerNetStored, pow_node);
        power = Math.floor(pow_node.powerNetIn - pow_node.powerNetOut) - min_power;
        if (pow_node.powerNetIn > power_max)
            power_max = Math.ceil(pow_node.powerNetIn);
        if (power < 0)
            print(error + "Need power\n" + white);
        // print("pnetin ", pow_node.powerNetIn, ", pnetout ", pow_node.powerNetOut, "\n");
        var percent = Math.floor(100 * power / power_max + 0.5);
        print("Power: ", Math.floor(power), "/", power_max , " (", percent, "%) :", Math.floor(pow_stored / 1000), "k/", Math.floor(pow_capacity / 1000), "k\n");
        if (pow_stored < pow_capacity)
            power = Math.floor((pow_stored - pow_capacity) / 100);
    }
    else {
        power = 0;
    }

    if (core) {
        core_max_rsrc = core.itemCapacity;
        link_low_rsrc = Math.floor(core_max_rsrc * 0.97);
        need_disass = enough_scrap &&
            ((tho != core_max_rsrc) || (tit != core_max_rsrc) || (graphite != core_max_rsrc) || (sand != core_max_rsrc));
        mass_enabled = true;
        if (!enough_coal) {
            print(need_color + "Need coal\n" + white);
            mass_enabled = false;
        }
        if (!enough_lead) {
            print(need_color + "Need lead\n" + white);
            mass_enabled = false;
        }
        if (!enough_copper) {
            print(need_color + "Need copper\n" + white);
            mass_enabled = false;
        }
        if (!enough_sand) {
            print(need_color + "Need sand\n" + white);
            mass_enabled = false;
        }
        if (!enough_pyr) {
            print(need_color + "Need pyratite[white]\n" + white);
            mass_enabled = false;
        }
        if (!enough_tit) {
            print(need_color + "Need titanium\n" + white);
            mass_enabled = false;
        }
        if (!enough_tho) {
            print(need_color + "Need thorium\n" + white);
        }
        if (!enough_sil) {
            print(need_color + "Need silicium\n" + white);
            mass_enabled = false;
        }
        if (!enough_spore) {
            print(need_color + "Need spore\n" + white);
            mass_enabled = false;
        }
        if (!enough_scrap) {
            print(need_color + "Need scrap\n" + white);
            mass_enabled = false;
        }
        if (!enough_blast) {
            print(need_color + "Need blast compound\n" + white);
            mass_enabled = false;
        }
        if (!enough_graphite) {
            print(need_color + "Need graphite\n" + white);
            mass_enabled = false;
        }
        if (!enough_surge) {
            print(need_color + "Need surge alloy\n" + white);
            mass_enabled = false;
        }
        if (!enough_plast) {
            print(need_color + "Need plastanium\n" + white);
            mass_enabled = false;
        }
    } else 
    {
        if (pow_node.powerNetCapacity) {
            if (pow_node.powerNetStored > 600)
                mass_enabled = true;
            else if (pow_node.powerNetStored < 10)
                mass_enabled = false;
        }
        else
            mass_enabled = true;
    }
    pow_node = undefined;

    printFlush(message);
}
hsaturn commented 7 months ago

The github pages website is updated on each release. To view the latest changes, you can checkout out the unreleased version

Yes I've edited the comment in the meanwhile

JeanJPNM commented 7 months ago

let me know if such comment is useless of if you're happy with it.

I'm happy with it, after all the unreleased version has to be released eventually.

My second guess is that jump optimization once implemented will reduce the oversize.

Indeed, it is one of the major problems with the current internal architecture of the compiler, as it cannot be implemented trivially without rewriting most of it from scratch (#222).

Having an option (if both generators are available) could help in this situation

A desirable improvement that may be implemented after the compiler rewrite.

hsaturn commented 7 months ago

May I suggest that a nth pass in order to polish the code is imho easy to code. I can write it in C++ in less than one hour. A first approach is to not remove the jumps, only to make them go to their final destination. A second one should be to also remove the jumps wich may seem easy also, even with renumbering, but one have to find all the set return_var @count in order to not break functions calls. And I understand now why you are not using labels, this would break function calls.

JeanJPNM commented 7 months ago

I do not prioritize the jump skipping optimization because it is already covered by #222. And I prefer to put more effort into finishing that pull request instead of duplicating the work just to bring a less flexible version of that feature to the main branch.

hsaturn commented 7 months ago

Ahem ... in fact I've finish to write the C++ code that resolves the jumps.

#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <fstream>
#include <algorithm>

using namespace std;

bool is_number(const string& s)
{
  return !s.empty() && all_of(s.begin(), s.end(), ::isdigit);
}

std::string getword(std::string& s, char sep=' ')
{
  if (s.length()==0) return "";
  std::string r;
  auto pos=s.find(sep);
  if (pos == std::string::npos) pos=s.length();
  r=s.substr(0, pos);
  s.erase(0,pos+1);
  return r;
}

int getint(std::string& s)
{
  string n = getword(s);
  if (is_number(n))
    return atol(n.c_str());
  else
    return -1;
}

int main(int argc, const char* argv[])
{
  set<int> always;        // list of always jumps
  map<int, int> jumps;    // line => destination
  map<int, string> code;  // rownum => original code
  ifstream f("test.asm");
  int row_nr = 0;
  while(f.good())
  {
    string row;
    getline(f, row);
    code[row_nr] = row;
    string op = getword(row);
    if (op == "jump")
    {
      int dest = getint(row);
      if (dest != -1)
      {
        jumps[row_nr] = dest;
        code[row_nr] = row;
        if (row == "always") always.insert(row_nr);
      }

    }
    // else if (op == "end")
      // jumps[row_nr] = 0;

    row_nr++;
  }

  cerr << "detected jumps: " << jumps.size() << ":" << endl;
  for(const auto& [n, d]: jumps)
    cerr << "   " << n << " goes to " << d << endl;
  cerr << endl;

  cerr << "result:" << endl;
  for(const auto& [row_nr, origin] : code)
  {
    if (jumps.find(row_nr) != jumps.end())
    {
      int end = jumps[row_nr];
      cerr << "resolving jump " << jumps[row_nr] << ", end=" << end;
      while(always.find(end) != always.end() && end != row_nr)
      {
        end = jumps[end];
        cerr << " -> " << end;
        if (end == row_nr) break;
      }
      if (end == row_nr) 
      {
        cerr << "jump at line row_nr is an infinite loop !" << endl;
        cout << origin << endl;
      }
      else
      {
        cerr << " -> " << end << endl;
        cout << "jump " << end << " " << origin << endl;
      }
    }
    else
      cout << origin << endl;
  }

  return 0;
}

compile: g++ jumps.cpp -o jumps

usage: ./jumps > result.asm

The source asm is test.asm. The result is written in result.asm

It works on the example of the bug report

test.asm:

jump 6 greaterThanEq c:1:4 10
jump 4 greaterThanEq c:1:4 5
set c:1:4 5
jump 5 always
set c:1:4 10
jump 7 always
set c:1:4 23
end

And result is

jump 6 greaterThanEq c:1:4 10
jump 4 greaterThanEq c:1:4 5
set c:1:4 5
jump 7 always
set c:1:4 10
jump 7 always
set c:1:4 23
end

for the curious, cerr output is

detected jumps: 4:
   0 goes to 3
   1 goes to 4
   3 goes to 5
   5 goes to 7

result:
   resolving jump 3, end=3 -> 5 -> 7 -> 7
   resolving jump 4, end=4 -> 4
   resolving jump 5, end=5 -> 7 -> 7
   resolving jump 7, end=7 -> 7
hsaturn commented 7 months ago

The github pages website is updated on each release. To view the latest changes, you can checkout out the unreleased version

Arf... of course, in the meantime, I've finish the c++ (very ugly) code. Anyway, this code will allow me to rejump actual ouput of mlogjs until #222 is out :-)

Here are some stats on a real case, (the big js I've posted here) 13 jumps are relocated all 13 jumps were doing only one indirection. Not so much !!!

I'm up for today (this is late here).