@felmue thanks great explanation and example. In your example, is there any issue restructuring it:
void setup()
{
...
    // First draw old count
    myCount = readEEPROM(EEPROM_ADDR, 0);
    snprintf(myBuf, 10,  "%04d ", myCount);
    CountPageSprite.drawString(myBuf, 20, 20);
    CountPageSprite.pushCanvas(0, 0, UPDATE_MODE_DU4);
}
void loop()
{
    // Then draw new count
    myCount++;
    writeEEPROM(EEPROM_ADDR, 0, myCount);
    snprintf(myBuf, 10, "%04d ", myCount);
    CountPageSprite.drawString(myBuf, 20, 20);
    CountPageSprite.pushCanvas(0, 0, UPDATE_MODE_DU4);
    delay(250); // required, else no update
    Serial.printf("Shutdown...\n");
    Serial.flush();
    M5.shutdown(10);
    // This part is only executed if running from USB power
    delay(1000);
    // Undo what shutdown did
    M5.RTC.clearIRQ();
    M5.enableMainPower();
    // Kill some time
    Serial.println("Should not be reached when battery powered 1");
    delay(10000);
    Serial.println("Should not be reached when battery powered 2");
}
to avoid needing to use the GOTO? I realise it will work perfectly well as-is. I think I would like to try and use the shutdown mode, as I can be asleep for most of the time, with updates once per minute at most.
I find it very unusual that the API docs don't seem to mention that  USB power vs battery power makes a difference to whether shutdown() will result in any change to the power state.
@ajb2k3 thanks for your reply. Please could you elaborate on what HV mode is? I had  a look at the Paper API for canvas and power/system but couldn't see anything relating to 'HV'.  Do any of the existing API functions already take care of this?