Big update to prettier-extension-monkeyc

I've posted about prettier-extension-monkeyc before, but I've added a bunch of new features that developers will probably like (well, I've been missing them, so maybe you have too).

The new features it implements for VSCode include:

  • Goto Definition. Point at a symbol, Ctrl/Cmd click, and it will take you to the definition. Or F12
  • Goto References. Right click on a symbol and select "Goto References". It will show you all the references. Or Shift-F12
  • Peek Definition/Peek References. Same as above, but in a popup window so you don't lose your place in the original document.
  • Rename Symbol. Right click on a local, function, class or module name, and select "Rename Symbol". It will rename all the references. It doesn't yet work for class members/methods.
  • Goto Symbol. Type Ctrl/Cmd-Shift-O and pick a symbol from the drop down (which has a hierarchical view of all symbols in the current file). This also appears as an outline across the top of the file.
  • Open Symbol By Name. Type Ctrl/Cmd-T, then start typing letters from a symbol name. A drop down will be populated with all matching symbols from anywhere in your project.

Older features include a prettier based formatter for monkeyc, and a monkeyc optimizer that will build/run/export an optimized version of your project.

[edit: My last couple of replies seem to have just disappeared, and the whole conversation seems to be in a jumbled order, so tldr: there's a new test-release at https://github.com/markw65/prettier-extension-monkeyc/releases/tag/v2.0.9 which seems to work for me on linux. I'll do more verification tomorrow, and push a proper update to the vscode store once I'm sure everything is working]

  • Very similar to my results. Mine goes from 10910 to 10379, which is a little better... but of course, I'm mostly finding and tuning the optimizations by looking at my app.

  • Doesn't that mean that you use inlining much less than I do? Inlining is still way more efficient than to remove these 2 bytes of argc.

  • Doesn't that mean that you use inlining much less than I do

    Probably - you're definitely making heavier use of inlining than I am - but I was really referring to the overall results of the post build optimization.

    But you're right - you save 108 bytes via the argc optimization, while I save 136. So you have 54 methods, while I have 68. Even allowing for my app being a little larger, you have fewer methods "per byte".

  • v2.0.58 is out.

    This version adds a Minimize Locals pass to the post build optimizer, very similar to the existing Minimize Locals pass in the source to source optimizer, but with a few advantages:

    • it can see and re-assign all the locals, including the implicit "self" parameter, and any catch variables
    • it doesn't treat variables as monolithic - so it can merge a portion of one variable with a portion of another, resulting in more things getting merged.
    • it preserves the variables names by updating the .debug.xml file, so that the debugger still shows the original variable names

    Overall this means that if you're using the post build optimizer, you probably want to disable "Minimize Locals" in the source to source optimizer.

    It also adds a Size Based Pre pass to the post build optimizer. Currently this only deals with literal values - so unlike the source to source optimizer's version, it won't help with globals, or member variables. But again, it has access to lots of things that just aren't visible in the source, so this ends up being pretty effective; typically getting a 1% or more reduction in the code size. Since this can also add lots of local variables, I've added an option so it can be disabled (eg if it caused stack overflows).

    Finally it adds a number of small improvements to control flow, and other optimizations which can also result in small code size wins.

  • Wow! It decreased the size by almost 500 bytes (of a 12kB app) 

  • Wow! It decreased the size by almost 500 bytes

    That's more than I would have expected :-)

    Do you know how much of that was from PRE (you can disable postBulidPRE to find out)?

    The only apps I've seen that get that kind of win use a lot of Layout resources - which get turned into code  like the following (you can see it in bin/gen/.../mcgen.mc):

        module Layouts {
            function MainLayout1(dc as Graphics.Dc) as Array<WatchUi.Drawable> {
                var rez_cmp_local_custom_drawable_18849468981183839977 = new Background1();
                var rez_cmp_local_text_clockTime = new WatchUi.Text({:identifier=>"clockTime", :color=>Graphics.COLOR_WHITE, :locX=>(240 * 0.5), :locY=>-3, :justification=>Graphics.TEXT_JUSTIFY_CENTER, :font=>Graphics.FONT_LARGE});
                var rez_cmp_local_text_labelAltitude = new WatchUi.Text({:identifier=>"labelAltitude", :color=>Graphics.COLOR_DK_GRAY, :locX=>70, :locY=>40, :justification=>Graphics.TEXT_JUSTIFY_CENTER, :font=>Graphics.FONT_SMALL});
                var rez_cmp_local_text_altitude = new WatchUi.Text({:identifier=>"altitude", :color=>Graphics.COLOR_BLACK, :locX=>95, :locY=>65, :justification=>Graphics.TEXT_JUSTIFY_RIGHT, :font=>Graphics.FONT_NUMBER_MEDIUM});
                var rez_cmp_local_text_unitAltitude = new WatchUi.Text({:identifier=>"unitAltitude", :color=>Graphics.COLOR_BLACK, :locX=>98, :locY=>77, :justification=>Graphics.TEXT_JUSTIFY_LEFT, :font=>Graphics.FONT_LARGE});
                var rez_cmp_local_text_totalAscent = new WatchUi.Text({:identifier=>"totalAscent", :color=>Graphics.COLOR_BLACK, :locX=>208, :locY=>41, :justification=>Graphics.TEXT_JUSTIFY_RIGHT, :font=>Graphics.FONT_LARGE});
                var rez_cmp_local_text_totalDescent = new WatchUi.Text({:identifier=>"totalDescent", :color=>Graphics.COLOR_BLACK, :locX=>208, :locY=>71, :justification=>Graphics.TEXT_JUSTIFY_RIGHT, :font=>Graphics.FONT_LARGE});
                var rez_cmp_local_text_labelElapsedTime = new WatchUi.Text({:identifier=>"labelElapsedTime", :color=>Graphics.COLOR_DK_GRAY, :locX=>70, :locY=>170, :justification=>Graphics.TEXT_JUSTIFY_CENTER, :font=>Graphics.FONT_SMALL});
                var rez_cmp_local_text_elapsedTime = new WatchUi.Text({:identifier=>"elapsedTime", :color=>Graphics.COLOR_BLACK, :locX=>110, :locY=>130, :justification=>Graphics.TEXT_JUSTIFY_RIGHT, :font=>Graphics.FONT_NUMBER_MEDIUM});
                var rez_cmp_local_text_labelCurrentHeartRate = new WatchUi.Text({:identifier=>"labelCurrentHeartRate", :color=>Graphics.COLOR_DK_GRAY, :locX=>185, :locY=>175, :justification=>Graphics.TEXT_JUSTIFY_RIGHT, :font=>Graphics.FONT_SMALL});
                var rez_cmp_local_text_currentHeartRate = new WatchUi.Text({:identifier=>"currentHeartRate", :color=>Graphics.COLOR_BLACK, :locX=>218, :locY=>115, :justification=>Graphics.TEXT_JUSTIFY_RIGHT, :font=>Graphics.FONT_NUMBER_HOT});
                return [rez_cmp_local_custom_drawable_18849468981183839977, rez_cmp_local_text_clockTime, rez_cmp_local_text_labelAltitude, rez_cmp_local_text_altitude, rez_cmp_local_text_unitAltitude, rez_cmp_local_text_totalAscent, rez_cmp_local_text_totalDescent, rez_cmp_local_text_labelElapsedTime, rez_cmp_local_text_elapsedTime, rez_cmp_local_text_labelCurrentHeartRate, rez_cmp_local_text_currentHeartRate] as Array<WatchUi.Drawable>;
            }
            ...
        }

    Basically lots of symbols, each one used over and over again, which is ideal for big savings from PRE.

    But of course, Layouts are horribly inefficient to start with; and even after these savings, it's much better to avoid them (which I'm sure you do).

  • 2.0.58:
    > Sizes for optimized-AntHRV.original-fenix6: code: 12650 data: 2984 <
    > Sizes for optimized-AntHRV-fenix6: code: 12183 data: 2984 <
    2.0.59 Minimize Locals+:
    > Sizes for optimized-AntHRV.original-fenix6: code: 12650 data: 2984 <
    > Sizes for optimized-AntHRV-fenix6: code: 11695 data: 2984 <
    2.0.59 Minimize Locals-, without Post build PRE:
    > Sizes for optimized-AntHRV.original-fenix6: code: 12672 data: 2984 <
    > Sizes for optimized-AntHRV-fenix6: code: 11907 data: 2984 <
    2.0.59 Minimize Locals-, with Post build PRE:
    > Sizes for optimized-AntHRV.original-fenix6: code: 12672 data: 2984 <
    > Sizes for optimized-AntHRV-fenix6: code: 11691 data: 2984 <
     

  • So about 216 bytes from PRE, and 276 from the other optimizations. I got 150 from PRE and about 20 from the others. I guess you're doing something the optimizer really likes.

    Unless... I don't want to worry you, but have you tested it much with this version? I'm really surprised that you'd get such big wins from the new control flow optimizations; unless you have some dead code that wasn't being removed previously, but is now...

  • haven't tested yet

    Ok, I was worried, so I installed the latest version of your Ant+HRV on my fenix5xplus, copied it to my laptop, and ran the post build optimizer on it. There's one function (I don't know its name) that gets dramatically smaller.

    There's a check for null early on, and the optimizer seems to think it's always going to be null, and deletes the rest of the function.

    I'm not 100% sure yet, but I can't see how the optimizer could possibly *know* that it's null - so I'm almost certain there's a bug. So for now, I'd recommend downgrading.

    Also, once I fix this, I think the improvement will only be half what you're currently seeing.