This optimization looks for certain expressions that can be performed more efficiently. Many optimizations for different instructions are available.
If the logic ID used in the lookup instruction is an integer constant, Mindcode searches for the appropriate item, liquid, block, or unit type with given ID and if it finds one, the instruction is replaced by a set instruction setting the output variable directly to the item, liquid, block, or unit type.
Whether the optimization will be applied or not depends on the value of the builtin-evaluation option:
none: the optimization is never applied,compatible: the optimization is only applied when the ID maps to a stable Mindustry content object,full: the optimization is always applied.
Several optimizations are available for the op instruction.
In some cases, the operation result doesn't depend on the other operand (a multiplication by zero is always zero regardless of the other operand). In other cases, the result is equal to the other operand (a multiplication by one or a subtraction of zero). All the performed replacements are listed in this table:
| Operation | Operator | First operand | Second operand | Result | Level | Note |
|---|---|---|---|---|---|---|
| mul | var | 1 | var | Basic | Commutative | |
| mul | var | 0 | 0 | Basic | Commutative | |
| div | var | 1 | var | Basic | ||
| div, idiv, mod | var | 0 | null | Basic | ||
| div, idiv, mod | 0 | var | 0 | Advanced | ||
| add, xor | var | 0 | var | Basic | Commutative | |
| sub | var | 0 | var | Basic | ||
| shl, shr | var | 0 | var | Advanced | ||
| shl, shr | 0 | var | 0 | Basic | ||
| or | |, or |
var | 0 | var | Advanced | Commutative |
| or | ||, or |
var | nonzero | 1 | Basic | Commutative |
| and, land | var | 0 | 0 | Basic | Commutative | |
| land | and |
var | nonzero | var | Advanced | Commutative |
var represents a variable with an arbitrary, unknown value. In the case of commutative operations, the optimization is applied also when the first and second operands, as given in the table, are swapped.
Some mlog operations are produced by different Mindcode operators. When the optimizations are only applied to operations corresponding to specific Mindcode operators, these operators are listed in the Operator column.
Important
Some optimizations applied to or, and, land, xor, shl and shr operations applied to non-integer or non-boolean values may produce different results from unoptimized code: unoptimized code would result into an integer value (or boolean value in case of land), while the optimized code may produce a non-integer value. Passing a non-integer/non-boolean value into these operators is unusual, but not impossible. These optimizations are therefore only performed on advanced level and can be turned off by setting the level to basic.
When both operands of the op instruction are known to have the same value, some operations always produce a fixed value. If this is the case, the operation is replaced by a set instruction setting the target variable to the fixed value:
equal,lessThanEq,greaterThanEq,strictEqual: sets the result totruenotEqual,lessThan,greaterThan: sets the result tofalsesub,xor: sets the result to0or,land: sets the result directly to the first operand, if the instruction doesn't represent the boolean version of the operation (||or&&),and,minandmax: sets the result directly to the first operand.
The boolean OR operator (||) guarantees to always produce a boolean value - 0 or 1. As mlog itself doesn't guarantee that, an additional instruction must be used to ensure the result is a boolean value:
volatile x = a || b;
compiles to:
op or *tmp1 :a :b
op notEqual .x *tmp1 false
This additional instruction can be removed in either of the following cases:
- the output of
||is used in a context where any nonzero value can be used to representtrue(i.e., any other boolean/logical operator, or a condition comparing the result to0orfalse), - both inputs of
||are guaranteed to be 0 or 1.
Examples:
#set target = 8m;
#set remarks = comments;
/// a || b is computed first, and as it is used in another `op or` instruction, the additional instruction is removed
volatile x = a || (b || c);
/// The && and ! operators (op land/op equal) are guaranteed to produce 0 or 1
volatile y = a && b || !c;
/// When used in an if, the value need not to be normalized
volatile z = a || b ? "yes" : "no";
compiles to:
# a || b is computed first, and as it is used in another `op or` instruction, the additional instruction is removed
op or *tmp0 :b :c
op or *tmp7 :a *tmp0
op notEqual .x *tmp7 false
# The && and ! operators (op land/op equal) are guaranteed to produce 0 or 1
op land *tmp2 :a :b
op equal *tmp3 :c false
op or .y *tmp2 *tmp3
# When used in an if, the value need not to be normalized
op or *tmp5 :a :b
select .z notEqual *tmp5 false "yes" "no"
In some cases, an expression consisting of several instructions can be simplified to use fewer instructions, while producing identical results.
floorinstruction applied to a result of a multiplication or a division by a constant. Combines the two operations into one integer division (idiv) operation. In the case of multiplication, the constant operand is inverted to become the divisor in theidivoperation.
The packcolor instruction is replaced with the resulting value when all its arguments are numeric constant. The result is encoded as a color literal.
The readarr/writearr instruction is a virtual instruction which represents internal array element access. When the index is a numeric constant, the instruction is replaced by a set instruction accessing the array element directly. When the index is out of bounds, a compile-time error is generated.
When the read instruction accesses a character in a constant string at a constant index, the instruction is replaced by a set instruction accessing the character directly.
The read and write instructions accessing variables in current processor using a constant name are replaced with a set instruction accessing the processor variable directly.
Important
This optimization ensures the creation of the corresponding processor variable, as it includes its name in the set instruction. Since the read and write instructions' behavior depends on whether the target variable exists in the processor or not, this optimization may alter the behavior of the program. For this reason, this optimization is only performed on advanced level.
Example:
#set target = 8m;
param a = "x";
param b = "y";
@this.write(1, a);
@this.write(1, b);
print(@this.read("x"));
print(@this.read(b));
compiles to:
set a "x"
set b "y"
write 1 @this a
write 1 @this b
print x
read *tmp1 @this b
print *tmp1
This program outputs 1null. Since the name of variable y is not a compile-time constant, the optimization is not applied for it, and the value read from y is null. x, on the other hand, is a compile-time constant, so the optimization is applied, and the value is actually written to the variable.
Several optimizations are applied to the select instruction:
selectinstruction with a constant condition is replaced by asetinstruction assigning the resulting value determined by the condition to the target variable. Note that in cases where the condition has identical operands, its value is also constant (see above).selectinstruction with identical true and false values is replaced by asetinstruction assigning the true value to the target variable, as the resulting value doesn't depend on the condition.selectinstruction with true/false values of1/0or0/1is replaced by anopinstruction with the same condition (this is done not just for better readability/shorter code, but also to allow further optimizations involving the createdopinstruction). If the true value is equal to zero, the condition is inverted to produce the correct result. The optimization doesn't happen for thestrictEqualconditions in that case, as it can't be inverted.
sensor var @this @x and sensor var @this @y are replaced by set var @thisx and set var @thisy respectively.
When the builtin-evaluation option is set to compatible or full, the following additional expressions are handled:
- If the
@constantin asensor var @constant @idinstruction is a known item, liquid, block, or unit type constant with a defined logic ID, the Mindustry's ID of the object is looked up and the instruction is replaced byset var <ID>, where<ID>is a numeric literal. - If the
@constantin asensor var @constant @nameinstruction is a known item, liquid, block, or unit type constant, the Mindustry's content name of the object is looked up and the instruction is replaced byset var <name>, where<name>is a string literal. This optimization takes place even whenbuiltin-evaluationis set tonone.
Some Mindustry content objects may have different logic IDs in different Mindustry versions (these objects are called unstable). For these objects, the above optimizations only happen when the builtin-evaluation option is set to full:
#set target = 7m;
#set builtin-evaluation = full;
println(lookup(:item, 18));
println(@tungsten.@id);
printflush(message1);
compiles to:
print "dormant-cyst\n19\n"
printflush message1
When compiled with builtin-evaluation set to compatible, the code is
lookup item *tmp0 18
print *tmp0
print "\n"
sensor *tmp1 @tungsten @id
print *tmp1
print "\n"
printflush message1
- All
setinstructions assigning a variable to itself (e.g.,set x x) are removed.
« Previous: Dead Code Elimination | Up: Code optimization | Next: Function Inlining »