-
Notifications
You must be signed in to change notification settings - Fork 0
Description
I noticed, that sometimes inlinling doesn't work as I expect it. I created simple test functions that cover all (I hope) cases I have.
Some may be a variation of the forbidden cases described in the wiki, and I don't realize that yet. Some of them may be caused by the issues with the optimizer. I hope you can explain the results I see.
The examples have the following structure: functions inline* are annotated with (:inline) and expected to be inlined and removed from the code, functions function* call these inline*s and expected to contain the body of the inlined function, functions aux* are some helper functions, functions expected* are the expected result of the inlining operation for corresponding inline* and function* functions.
I tried to toggle prettierMonkeyC.iterateOptimizer, but it doesn't seem to affect the results.
import Toybox.Lang;
(:keep)
function function1(b as Boolean) as Number {
// While inlining inline1: This function can only be inlined in statement, assignment, if or return contexts
// While inlining inline1: Function didn't end with a return statement
return inline1(b);
}
(:keep)
function function2(b as Boolean) as Number {
// While inlining inline1: This function can only be inlined in statement, assignment, if or return contexts
// While inlining inline1: Function didn't end with a return statement
var x = inline1(b);
return x;
}
(:inline)
function inline1(b as Boolean) as Number {
if (b) {
return 1;
} else {
return 0;
}
}
(:keep)
function expected1(b as Boolean) as Number {
if (b) {
return 1;
} else {
return 0;
}
}
(:keep)
function expected2(b as Boolean) as Number {
// Either this or as in "expected1" after all other optimizations
var x;
if (b) {
x = 1;
} else {
x = 0;
}
return x;
}Generally, the optimizer can't inline a function unless it has a single return statement, and the return statement must be last, both in lexical order and in control flow order. This is because
MonkeyCdoesn't supportgoto, making it very hard to mimic the control flow produced by multiple returns.
I guess, it contradicts this requirement, but I don't understand, why it can't be inlined. Though inline1 has multiple return statements, it is pretty easy to do it manually.
I see the same results for switch with direct returns in all cases with default at the end in the inline* function.
import Toybox.Lang;
(:keep)
function function3(i as Number) as Number {
// While inlining inline3: This function can only be inlined in statement, assignment, if or return contexts
return inline3()[i];
}
(:inline)
function inline3() as Array<Number> {
var a = [0] as Array<Number>;
return a;
}
(:keep)
function expected3(i as Number) as Number {
return ([0] as Array<Number>)[i];
}Inlining works with direct return [0] as Array<Number>;. I'd expect iterateOptimizer to do the job on the second pass.
import Toybox.Lang;
(:keep)
function function4(b as Boolean, x as Number) as Float {
// While inlining inline4: This function can only be inlined in statement, assignment, if or return contexts
return b ? 0.0 : inline4(x as Number);
}
(:inline)
function inline4(x as Number?) as Float {
return x != null ? x.toFloat() : 0.0;
}
(:keep)
function expected4(b as Boolean, x as Number) as Float {
return b ? 0.0 : (x != null ? x.toFloat() : 0.0);
}It works with inline4(x) instead of inline4(x as Number). Originally x is declared as Number? in function4, but it is guaranteed to be Number, if b is false, so I use cast as Number.
import Toybox.Application;
import Toybox.Lang;
class Class5 {
private var s as String = inline5();
// The inline function inline5 was not removed from the program
(:inline)
private function inline5() as String {
return Application.loadResource(Rez.Strings.AppName) as String;
}
}
class ExpectedClass5 {
private var s as String = Application.loadResource(Rez.Strings.AppName) as String;
}There are no While inlining ... message with the explanation for this case.
import Toybox.Lang;
import Toybox.System;
class Class61 {
private static var x as Number = 0;
(:keep)
static function classMethod() as Number {
return x;
}
}
class Class62 {
// The inline function classMethod was not removed from the program
(:inline)
static function classMethod() as Number {
return Class61.classMethod();
}
}
class Class63 {
(:keep)
function function6() as Number {
return Class62.classMethod();
}
}
class ExpectedClass63 {
(:keep)
function function6() as Number {
return Class61.classMethod();
}
}It works as expected, if I rename Class61.classMethod() to something else.
import Toybox.Lang;
import Toybox.Time;
import Toybox.Time.Gregorian;
(:keep)
function function7() as Number {
// While inlining inline7: This function can only be inlined in statement, assignment, if or return contexts
return inline7(Time.now()).value();
}
(:inline)
function inline7(time as Moment) as Moment {
return aux7(time);
}
(:keep)
function aux7(time as Moment) as Moment {
var timeInfo = Gregorian.info(time, Time.FORMAT_SHORT);
return Gregorian.moment({
:year => timeInfo.year,
:month => timeInfo.month as Number,
:day => timeInfo.day,
:hour => 0,
:minute => 0,
:second => 0,
});
}
(:keep)
function expected7() as Number {
return aux7(Time.now()).value();
}It works, if I directly return result of inline7 function call from function7 (with updated return type in function7 declaration, ofc).
import Toybox.Lang;
import Toybox.System;
(:keep)
function function8(x as Number) as Void {
// While inlining inline8: This function can only be inlined in statement, assignment, if or return contexts
aux8(inline8(x));
}
(:inline)
function inline8(x as Number) as Number {
var y = x;
return x + y;
}
(:keep)
function aux8(x as Number) as Void {
System.println(x);
}
(:keep)
function expected8(x as Number) as Void {
// Either this or "aux8(x + x)" after all other optimizations
var y = x;
var z = x + y;
aux8(z);
}I see no reasons for it not to be inlined, especially with enabled iterateOptimizer.
Thank you!