Up to Main Index                             Up to Journal for March, 2025

                    JOURNAL FOR SATURDAY 29TH MARCH, 2025
______________________________________________________________________________

SUBJECT: Optimizing the ‘is’ built-in and invariant expressions
   DATE: Sat 29 Mar 20:44:14 GMT 2025

I recently made a small change to Mote’s optimizer that can reduce memory and
improve performance, if only a little, when using the ‘is’ built-in with
invariant expressions.

Invariant expressions are key to compiler optimization because their values
are known at compile time. This allows the compiler to pre-calculate these
expressions and replace the expression with its result, avoiding runtime
calculations. This saves memory and improves performance.

The optimizer analyses code at compile time to identify and replace invariant
expressions. Typically, invariant expressions are those that only have
constant or literal operands. However, more complex expressions can sometimes
be determined at compile time.

As an example, consider this code to calculate the white-space padding needed
to center a title within the given width:


    const Title = "My Journal"
    const Width = 40
    println(" " * ((Width - len Title) / 2), Title)


The expression on the println line is invariant as it only uses constants and
literals. The variables ‘Width’ and ‘Title’ are constants which are invariant,
therefore the length of ‘Title’ is also invariant. The number 2 is a literal
and therefore invariant. As all of the values in the expression are invariant
the optimizer can pre-calculate the expression and replace it with the result.
This results in the following equivalent optimized code:


    const Title = "My Journal"
    const Width = 40
    println "               My Journal"


How does this relate to Mote’s ‘is’ built-in?

The ‘is’ built-in is a concise way to express conditional statements where you
want to execute a single statement based on a condition. If the condition of
the ‘is’ is true the following statement is executed; otherwise, it’s skipped.

I love the ‘is’ built-in and use it a lot. Here is an example making good use
of ‘is’ — a classic FizzBuzz program. Using ‘is’ allows for concise and
readable conditional logic within the main loop:


    const Limit = 35

    for x=1; x<=Limit; x++
      s = ""
      is x % 3 == 0; s += "Fizz"
      is x % 5 == 0; s += "Buzz"
      is s == ""; s =  x
      print s ", "
    next
    println "…"


When run, the above code produces:


   1,  2, Fizz,  4, Buzz, Fizz,  7,  8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz,
  16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz,
  31, 32, Fizz, 34, Buzz, …


If the conditional part of an ‘is’ statement is invariant we can optimize it.
Here is an example of invariant ‘is’ conditions used with a Debug constant:


    const Debug = true
    is !Debug; println "Debug is off"
    is  Debug; println "Debug is on"


The key point here is that Debug is a constant, therefore the condition in the
‘is’ statement is an invariant expression and can be known at compile time.

In this example ‘Debug’ is a constant that will always be either true or
false. This means that the ‘is’ condition is invariant and known at compile
time. This results in the optimizer rewriting the code in one of two ways at
compile time:


           # Optimized code when Debug is true
           const Debug = true
           println "Debug is on"


           # Optimized code when Debug is false
           const Debug = false
           println "Debug is off"


Either way, the code is smaller and more performant.

The optimizer will analyse the condition within the `is` statement. If the
condition is always true (‘Debug’ is set to true), the entire ‘is’ statement
is removed. If the condition is always false (‘Debug’ is set to false), the
entire ‘is’ statement and the following statement are also removed. If the
condition was not constant, it is not optimized.

The optimizer is very simple and does have limitations. Only invariants using
constant or literal primitive values like numbers, booleans and strings are
optimized. For this reason the following would not be optimized:


    const Debug = true
    println "Debug is " [bool](false "off", true "on")[Debug]


The main limiting factor is being able to analyse complex literals. A number,
boolean or string is a single value, whereas a literal map or array is made up
of multiple values. Being able to optimize constant or literal maps and arrays
is an area left for future improvement.

--
Diddymus


  Up to Main Index                             Up to Journal for March, 2025