Pre-device const definitions and finer-grained runtime exclusions

Merry Christmas, Happy Festivus, and a joyous end to the fourth fiscal quarter (as my buddy would say).

Since Monkey C is rather space-inefficient, in many situations it is advantageous to implement code paths that are specific to a given device, and/or define device-specific layout constants or other tweaks. It seems that runtime exclusions have been discussed quite a bit, but these only allow excluding a function, and often require a corresponding "negated" exclusion to pull in an alternate implementation on all devices to which the exclusion needs to not apply. Exclusions "work" but incur the space penalty associated with adding a new function. I realize also that 'has' can be used to detect an exclusion (rather than having to define an alternate implementation) but this too has a cost.

As for per-device constants, it seems we can define them using device properties, but these "constants" aren't known at compile time, and are thus unavailable to the optimizer. The overhead for querying them is nontrivial, as well.

So, I've been thinking..... is it possible to solve some of these problems using per-device const definitions?

Hear me out.

The .jungle file already allows us to specify multiple source paths. What if we put our "main" .mc file in sources/, then create a pile of device-specific .mc files in constants/devicename/constants.mc, fill these with const WHATEVER=foo;, then reference these constants from the main implementation?

Furthermore, if we define const DEVICE_NEEDS_FEATURE_X = false in a device-specific constants file, our shared implementation file can then do this:

if (DEVICE_NEEDS_FEATURE_X) {
   do some stuff
}

and since DEVICE_NEEDS_FEATURE_X is known at compile time, the compiler is smart enough to exclude the body of the 'if' if the feature X is set to false.

The device-specific constant files could even be auto-generated, without have to involve (gaslight-prone) codegen junk in our main implementation.

Would something like this work, or have I been getting even less sleep than usual?

Something along the line of:

Jungle:

fenix5xplus.sourcePath                  = $(sources-common);constants/fenix5xplus
fenix6pro.sourcePath                    = $(sources-common);constants/fenix6pro
fenix6spro.sourcePath                   = $(sources-common);constants/fenix6spro
fenix6xpro.sourcePath                   = $(sources-common);constants/fenix6xpro
fenix843mm.sourcePath                   = $(sources-common);constants/fenix843mm
fenix847mm.sourcePath                   = $(sources-common);constants/fenix847mm

constants/fenix5xplus/constants.mc

const ARC_DIVISION_WIDTH = 3;
const HAVE_ELEVATION_UNITS = true;

sources-common/datafield.mc:

if (HAVE_ELEVATION_UNITS) {   // Known at build-time; one half gets optimized out
    elev_units = System.getDeviceSettings().elevationUnits;
} else {
    elev_units = System.getDeviceSettings().distanceUnits;
}

dc.drawArc(foo, bar, start, start + ARC_DIVIDION_WIDTH);
etc

Would something like this work, or is there a better way, that doesn't use more memory? Thanks!

  • I do exactly that. Generate mc constants per device and jungle file in python from a jungle template and the compiler.json and simulator.json files. Some of it is here (though my latest improvements are not committed yet): github.com/.../monkey-generator

  • Have you looked at using "has" ? As of the 7.1.0 SDK, by default is they are resolved at compile time.  Nothing needed in jungles.

    Have you looked at using resource overrides, where the compiler will use different resources based on the device , the screen, etc.For things like (ayouts, custom fonts  See the strings sample in the SDK.  Again, nothing in jungles.  See https://developer.garmin.com/connect-iq/core-topics/resources/#resources

    And for some things, you can do a simple calc in onLayout maybe using width and height of the dc.

    Another thing to consider is ongoing support.  New devices, new system levels (there are rumors of System 8 based on beta FW for f8 device)

    There are really only a couple cases where I do anything special in jungles

    1) on devices that can publish complications

    2) on devices with onboard maps

  • I feel that resource overrides work at compile time, but they're only retrieved at runtime. So, you still have to have the overhead of loading the resource, and the code operating on the value being loaded can only be optimized to a limited degree.

    "Has" being compile-time is interesting, but doesn't really solve the problem where you're trying to perform minor visual tweaks (which cannot be expressed as resources) or change behavior based on some device attribute that isn't tied to an API being available (or has more than two states, making exclusion annotations annoying)?

  • It also works for sources: source-X/ where you can put your constants or code

  • Thanks! I had been looking for a solution to this problem online, but haven't come across a definitive answer. Good to know this seems reasonable. Hopefully the next person searching for this stuff won't have to reinvent the wheel themselves.